在上一篇文章中,我们已经对哈希的基础有了一个大概的认识,但是对其实现还没有做具体的解释,在这篇文章中,我们将对这一部分做出一个详细的解释。
有关哈希基础,
请参考上篇https://blog.csdn.net/aaronlanni/article/details/79701843
一、实现静态的哈希表
下面,将给出静态实现:
//静态
#define MAX_SIZE 10
typedef enum State{ EMPTY, DETELE, EXIST };
template<class K>
struct Elem
{
K _key;
State _state;
};
template<class K,bool IsLine=true>
class HashTable
{
public:
HashTable()
{
//清空每个位置
for (int i = 0; i < MAX_SIZE; ++i)
{
_hashtable[i]._state = EMPTY;
}
_size = 0;
}
//插入元素
bool Insert(const K&key)
{
//负载因子
if (_size * 10 / MAX_SIZE>7)
return false;
size_t HashAdder = Func(key);
size_t stateAdder = HashAdder;
size_t i = 0;
//找空位置
while (_hashtable[HashAdder]._state != EMPTY)
{
if (_hashtable[HashAdder]._state == EXIST&&_hashtable[HashAdder]._key == key)
return false;
if (IsLine)
LineCheck(HashAdder);
else
SecondCheck(HashAdder, i++);
if (HashAdder == stateAdder)
return false;
}
//插入元素
_hashtable[HashAdder]._key = key;
_hashtable[HashAdder]._state = EXIST;
++_size;
return true;
}
//查找元素
int Find(const K&key)
{
//在哈希表中找对应的位置
size_t HashAdder = Func(key);
size_t i = 0;
//找空位置
while (_hashtable[HashAdder]._state != EMPTY)
{
if (_hashtable[HashAdder]._key == key)
return _hashtable[HashAdder]._key;
if (IsLine)
LineCheck(HashAdder);
else
SecondCheck(HashAdder, i++);
}
return -1;
}
//删除元素
bool DElete(const K&key)
{
size_t HashAdder = Func(key);
int ret = Find(key);
if (-1 != ret)
{
_hashtable[HashAdder]._state = DETELE;
--_size;
return true;
}
return false;
}
size_t Size()
{
return _size;
}
bool Empty()
{
return _size == 0;
}
private:
//线性探测
void LineCheck(size_t& HashAdder)
{
++HashAdder;
if (HashAdder >= MAX_SIZE)
HashAdder = 0;
}
//二次探测
void SecondCheck(size_t HashAdder,size_t i)
{
HashAdder = HashAdder + ((i << 1) + 1);
if (HashAdder >= MAX_SIZE)
HashAdder %= MAX_SIZE;
}
private:
//哈希函数
size_t Func(const K&key)
{
return key%MAX_SIZE;
}
private:
Elem<K> _hashtable[MAX_SIZE];
size_t _size;
};
二、实现动态的哈希表
在静态哈希表的基础上,由于利用静态存储,很容易将哈希表存满,因此,利用动态的哈希表,从而可以实现不断的给哈希中存储元素,但是在上面的基础上,我们难免还会想到,在哈希中,我们不仅仅是对整型数值的存储,在必要的时候,我们还需要存储字符串,因此在对于字符串的存储中,我们对哈希地址的计算便是一个大问题,因此将字符串转换为整数,然后对其哈希地址进行计算即可。
(对于有关字符串转整数,可以采取两种措施:1、对其字符串取地址,从而可以取得字符串的整数形式2、利用字符串转整数的函数,同时在此基础上,且利用类与特化从而实现字符串的转换即可)
在实现动态的哈希表之时,利用除留余数法即可。
基本实现如下所示:
comm.hpp
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<string.h>
#include<iostream>
using namespace std;
// 使用素数表对齐做哈希表的容量,降低哈希冲突
const int _PrimeSize = 28;
static const unsigned long _PrimeList[_PrimeSize] =
{
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
//除留余数法
size_t GetNextPrime(size_t num)
{
for (int i = 0; i < _PrimeSize; ++i)
{
if (_PrimeList[i] > num)
return _PrimeList[i];
}
//走到最后,仍然没有找到
return _PrimeList[_PrimeSize - 1];
}
static size_t BKDRHash(const char * str)
{
unsigned int seed = 131; // 31 131 1313 13131 131313
unsigned int hash = 0;
while (*str)
{
hash = hash * seed + (*str++);
}
return (hash & 0x7FFFFFFF);
}
//转换函数(整数)
template<class K>
class KeyToIntDef
{
public:
size_t operator()(const K&key)
{
return key;
}
};
//字符串,需要特化
class StringToInt
{
public:
size_t operator()(const string& key)
{
return BKDRHash(key.c_str()); //key.c_str()将其
}
};
hashtable.hpp
//可以存取字符串
#include"Common.hpp"
//如果需要存取字符串,则需要在求取哈希地址的时候,需要转换,利用仿函数
template<class K>
struct Elem
{
K _key;
State _state;
};
template<class K, class KeyToInt = KeyToIntDef<int>, bool IsLine = true>
class HashTable
{
public:
HashTable(size_t capacity = 10)
{
capacity = GetNextPrime(capacity);//获得的哈希表的长度
_hashtable = new Elem<K>[capacity];
_capacity = capacity;
for (size_t i = 0; i < _capacity; ++i)
_hashtable[i]._state = EMPTY;
_size = 0;
}
//插入元素
bool Insert(const K&key)
{
CheckCapacity();
size_t HashAdder = Func(key);
size_t stateAdder = HashAdder;
size_t i = 1;
//找空位置
while (_hashtable[HashAdder]._state != EMPTY)
{
if (_hashtable[HashAdder]._state == EXIST&&_hashtable[HashAdder]._key == key)
return false;
if (IsLine)
LineCheck(HashAdder);
else
SecondCheck(HashAdder, i++);
if (HashAdder == stateAdder)
return false;
}
//插入元素
_hashtable[HashAdder]._key = key;
_hashtable[HashAdder]._state = EXIST;
++_size;
return true;
}
//查找元素
int Find(const K&key)
{
//在哈希表中找对应的位置
size_t HashAdder = Func(key);
size_t i = 1;
//找位置
while (_hashtable[HashAdder]._state != EMPTY)
{
if (_hashtable[HashAdder]._state == EXIST)
{
if (_hashtable[HashAdder]._key == key)
return HashAdder;
}
if (IsLine)
LineCheck(HashAdder);
else
SecondCheck(HashAdder, i++);
}
return -1;
}
//删除元素
bool DElete(const K&key)
{
size_t HashAdder = Func(key);
int ret = Find(key);
if (-1 != ret)
{
_hashtable[HashAdder]._state = DETELE;
--_size;
return true;
}
return false;
}
size_t Size()
{
return _size;
}
bool Empty()
{
return _size == 0;
}
~HashTable()
{
if (_hashtable)
{
_capacity = 0;
_size = 0;
delete[] _hashtable;
}
}
private:
//交换函数
void Swap(HashTable<K,KeyToInt,IsLine>& ht)
{
swap(_hashtable, ht._hashtable);
swap(_size, ht._size);
swap(_capacity, ht._capacity);
}
//增容函数
void CheckCapacity()
{
if (_size * 10 / _capacity >= 5)
{
size_t newCapacity = GetNextPrime(_capacity);
HashTable<K,KeyToInt,IsLine> newHt(newCapacity);//创建新的哈希表
//搬移元素
for (size_t i = 0; i < _capacity; ++i)
{
if (_hashtable[i]._state == EXIST)
newHt.Insert(_hashtable[i]._key);
}
Swap(newHt);
}
}
//线性探测
void LineCheck(size_t& HashAdder)
{
++HashAdder;
if (HashAdder >= _capacity)
HashAdder = 0;
}
//二次探测
void SecondCheck(size_t HashAdder, size_t i)
{
HashAdder = HashAdder + ((i << 1) + 1);
if (HashAdder >= _capacity)
HashAdder %= _capacity;
}
private:
size_t Func(const K&key)
{
return KeyToInt()(key)%_capacity;
}
private:
Elem<K> * _hashtable;
size_t _size;
size_t _capacity;
};
有关闭散列的有关知识,大概就这么多了,希望大家一起进步!!!
只有不停的奔跑,才能不停留在原地!!!