分离链接散列算法的缺点是使用一些链表,由于给新单元分配地址需要时间,因此这就导致算法的速度有些缓慢,同时算法实际上还需要第二种数据结构的实现。下面就用探测散列表来实现哈希表。
1.线性探测
在线性探测中,函数f是i的线性函数,一般情况下f(i) = i,这相当于逐个探测每个单元(使用回绕)来查找出空单元。
2.平方探测
平方探测是消除线性探测中一次聚集问题的冲突解决方法。平方探测就是冲突函数为二次函数的探测方法。流行的选择时f(i) = i²,对于线性探测,让散列表近乎填满元素是个坏主意,因为此时表的性能会降低。对于平方探测,情况甚至更糟:一旦表被填满超过一半,若表的大小不是素数,那么甚至在表被填满一半之前,就不能保证找到空的元素了,这是因为最多只有表的一半可以用做解决冲突的备选位置。
下面为探测散列表的代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
template<typename HashedObj>
class HashTable
{
public:
explicit HashTable(int size = 101) : array(size)
{
makeEmpty();
currentSize = size;
}
bool contains(const HashedObj & x) const;
void makeEmpty();
bool insert(const HashedObj & x);
bool remove(const HashedObj & x);
enum EntryType {ACTIVE,EMPTY,DELETED};
private:
struct HashEntry
{
HashedObj element;
EntryType info; //状态,惰性删除用
HashEntry(const HashedObj & e = HashedObj(),EntryType i = EMPTY) :element(e),info(i) {}
};
vector<HashEntry> array;
int currentSize;
bool isActive(int currentPos) const;
int findPos(const HashedObj & x) const;
void rehash();
int myhash(const HashedObj & x) const;
};
bool Prime(int res)
{
for(int i=2;i<res/2;i++)
{
if(res % i == 0)
{
return true;
break;
}
}
return false;
}
int nextPrime(int num)
{
while(1)
{
if(Prime(num) == false)
{
return num;
break;
}
else
{
num++;
}
}
}
int hash(const string &key)
{
int hashVal = 0;
for(int i = 0; i < key.length(); i++)
{
hashVal = hashVal * 37 + key[i];
}
return hashVal;
}
int hash(int key)
{
return key;
}
template<typename HashedObj>
int HashTable<HashedObj>::myhash(const HashedObj & x) const
{
int hashVal = hash(x);
hashVal %= array.size(); //theLists.size()为总容量
if(hashVal < 0)
{
hashVal += array.size();
}
return hashVal;
}
template<typename HashedObj>
void HashTable<HashedObj>::makeEmpty()
{
currentSize = 0;
for(int i=0;i<array.size();i++)
{
array[i].info = EMPTY;
}
}
template<typename HashedObj>
bool HashTable<HashedObj>::contains(const HashedObj & x) const
{
return isActive(findPos(x));
}
template<typename HashedObj>
int HashTable<HashedObj>::findPos(const HashedObj & x) const
{
int offset = 1;
int currentPos = myhash(x);
/*
如果该位置已存在元素,或者插入的值为没有出现过的值,以下为平方探测的方法 递推式为f(n) = f(n-1) + 2n - 1
*/
while(array[currentPos].info != EMPTY && array[currentPos].element != x) //本行的两个条件顺序很重要,切勿交换
{
currentPos += offset; //Compute ith probe
offset += 2;
if(currentPos >= array.size())
{
currentPos -= array.size(); // 防止超过
}
}
return currentPos;
}
template<typename HashedObj>
void HashTable<HashedObj>::rehash()
{
vector<HashEntry> oldArray = array;
//建立两倍的空间
array.resize(nextPrime((2*oldArray.size())));
//cout<<array.size()<<endl;
for(int j=0;j<array.size();j++)
{
array[j].info = EMPTY;
}
currentSize = 0;
for(int i=0;i<oldArray.size();i++)
{
if(oldArray[i].info == ACTIVE) // 前面的数据赋值给新的空间
insert(oldArray[i].element);
}
}
template<typename HashedObj>
bool HashTable<HashedObj>::isActive(int currentPos) const
{
return array[currentPos].info == ACTIVE; //判断是否存在或者不存在
}
template<typename HashedObj>
bool HashTable<HashedObj>::insert(const HashedObj & x)
{
int currentPos = findPos(x);
if(isActive(currentPos)) //表示已经存在
{
return false;
}
array[currentPos] = HashEntry(x,ACTIVE); //HashEntry的数组对象,每个数组元素都有它的元素和info
if(++currentSize > array.size() / 2) //如果数据量已经超过一半了,那就要当前空间要扩容2倍
rehash();
return true;
}
template<typename HashedObj>
bool HashTable<HashedObj>::remove(const HashedObj & x)
{
int currentPos = findPos(x);
if(!isActive(currentPos)) //如果不存在
{
return false;
}
array[currentPos].info = DELETED;
return true;
}
int main()
{
HashTable<int> b;
b.insert(1);
b.insert(10);
b.insert(100);
b.insert(9);
b.insert(69);
b.insert(57);
if(b.contains(100) && b.contains(57))
{
cout<<"yes"<<endl;
}
else
{
cout<<"no"<<endl;
}
if(b.insert(57))
{
cout<<"insert succeed!"<<endl;
}
else
{
cout<<"repeat!"<<endl;
}
if(b.remove(69))
{
cout<<"remove succeed!"<<endl;
}
else
{
cout<<"The element not exist in HashTable";
}
b.makeEmpty();
if(b.contains(1) || b.contains(10) || b.contains(100))
{
cout<<"The HashTable is not empty!"<<endl;
}
else
{
cout<<"It is empty!"<<endl;
}
return 0;
}