它通过一个关键值的函数将所需的数据映射到表中的位置来访问数据,这个映射函数叫做散列函数,存放记录的数组叫做散列表。
2.散列函数的构造方法
(1).直接定址法--取关键字的某个线性函数为散列地址,Hash(Key)= Key 或 Hash(Key)= A*Key + B,A、B为常数。
优点:简单、均匀,也不会产生冲突,但需要事先知道关键字的分布情况,适合查找表较小且连续的情况。
(2).除留余数法--取关键值被某个不大于散列表长m的数p除后的所得的余数为散列地址。Hash(Key)= Key % P。
选p的技巧:p为小于或等于表长(最好接近于m)的最小质数或不包含小于20质因子的合数。
(3).平方取中法
适合不知道关键字分布,而且位数又不是很大的情况。
(4). 折叠法
适合不知道关键字分布,而且位数较多的情况
(5). 随机数法
适合关键字的长度不等。
(6). 数学分析法
适合关键字位数比较多,事先知道关键字的分布且关键字的若干位分布较均匀。
3. 处理散列冲突的方法
不同的Key值经过哈希函数Hash(Key)处理以后可能产生相同的值哈希地址,我们称这种情况为哈希冲突。任意的散列函数都不能避免产生冲突。
(1).闭散列方法-开放定址法概念:一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
. 线性探测
发生冲突时,就找它加1的散列地址,直到找到为止。
(Hash(key)+0),(Hash(key)+1),(Hash(key)+2),.....,(Hash(key)+i).
. 二次探测
发生冲突时,就找它加1的平方的散列地址,如果也冲突就找它加2的平方的散列地址,等等一直到找到为止。
(Hash(key)+0),(Hash(key)+1^2),(Hash(key)+2^2),....,(Hash(key)+i^2).
. 随机探测
(2).开链法/拉链法(哈希桶)实现:
第一种方法:用除留余数法构造散列函数,用开放定址法处理哈希冲突。
补充:代码中仿函数和特化任选一个。
enum State
{
EXIST,
EMPTY,
DELETE,
};
//仿函数==>函数对象
struct __StringHashFunc
{
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);
}
size_t operator()(const string& s)
{
return BKDRHash(s.c_str());
}
};
template<class K>
struct __HashFunc
{
size_t operator()(const K& key)
{
return key;
}
};
//特化
//template<class K>
//struct __HashFunc
//{
// size_t operator()(K& key)
// {
// return key;
// }
//};
//template<>
//struct __HashFunc<string>
//{
// 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);
// }
// size_t operator()(const string& s)
// {
// return BKDRHash(s.c_str());
// }
//};
template<class K,class V>
struct HsahTableNode
{
K _key;
V _value;
State _state;
HsahTableNode(const K& key,const V& value)
:_key(key)
,_value(value)
,_state(EMPTY)
{}
HsahTableNode()
:_state(EMPTY)
{}
};
template<class K,class V,class HashFunc=__HashFunc<K>>
class HashTable
{
public:
typedef HsahTableNode<K,V> Node;
HashTable()
:_size(0)
{
_tables.resize (_GetNextPrime(0));
}
bool Insert(const K& key,const V& value)
{
_Chicksize();
size_t index=_HashFunc(key); //找的key的下标
while(_tables[index]._state==EXIST)
{
if(_tables[index]._key==key)
return false;
++index; //线性探测--减少哈希冲突
if(index==_tables.size())
index=0;
}
_tables[index]._key=key;
_tables[index]._value=value;
_tables[index]._state=EXIST;
return true;
}
bool Remove(const K& key)
{
//第一种方法
/*size_t index=_HashFunc(key);
while(_tables[index]._state!=EMPTY)
{
if(_tables[index]._key==key)
{
_tables[index]._state=DELETE;
return true;
}
else
++index;
}
return false;*/
//第二种方法
Node* ret=Find(key);
if(ret)
{
ret->_state =DELETE;
return true;
}
return false;
}
Node* Find(const K& key)
{
size_t index=_HashFunc(key);
while(_tables[index]._state!=EMPTY)
{
if(_tables[index]._key==key)
{
return &_tables[index];
}
else
++index;
}
return NULL;
}
protected:
size_t _HashFunc(const K& key)
{
HashFunc hf;
return hf(key)%_tables.size (); //除留余数法
}
void _Chicksize()
{
if(_tables.size ()==0||_size*10/_tables.size()>=8) //为空或大于负载因子
{
size_t NewSize=_GetNextPrime(_tables.size ());
HashTable<K,V> Newhashtable;
Newhashtable._tables .resize (NewSize);
for(size_t i=0;i<_tables.size ();++i) //复制节点
{
if(_tables[i]._state==EXIST)
{
Newhashtable.Insert (_tables[i]._key,_tables[i]._value);
}
}
Swap(Newhashtable);
}
}
void Swap(HashTable<K,V>& Newhashtable)
{
_tables.swap (Newhashtable._tables); //交换vector的指针
swap(_size,Newhashtable._size ); //交换指向的_size
}
int _GetNextPrime(size_t size) //<span style="color: rgb(0, 128, 0); font-family: Arial, Helvetica, sans-serif;">使用素数表对齐做哈希表的容量,降低哈希冲突</span>
{
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
};
for(size_t i=0;i<_PrimeSize;++i)
{
if(size<_PrimeList[i])
{
return _PrimeList[i];
}
}
}
private:
vector<Node> _tables;
size_t _size;
};
void TestHsahTableNode()
{
//HashTable<int,int> ht;
//int arr[]={89,18,49,58,9,111};
//for(size_t i=0;i<6;++i)
//{
// ht.Insert (arr[i],i);
//}
//cout<<ht.Remove (111)<<endl; //1
//cout<<ht.Remove (55)<<endl; //0
//统计字符出现的次数
HashTable<string,int,__StringHashFunc> ht1;
char* str[]={"aaa","aaaa","aaa","ddd"};
for(size_t i=0;i<sizeof(str)/sizeof(str[0]);++i)
{
HsahTableNode<string,int>* ret=ht1.Find (str[i]);
if(ret) //存在
{
//ret->_value ++;
(*ret)._value ++;
}
ht1.Insert (str[i],0);
}
}
第二种方法:用除留余数法构造散列函数,用开链法处理哈希冲突。
//开链法
template<class K,class V>
struct KVNode
{
K _key;
V _value;
KVNode<K,V>* _next;
KVNode(const K& key,const V& value)
:_key(key)
,_value(value)
,_next(NULL)
{}
};
template<class K,class V>
class HashTable
{
typedef KVNode<K,V> Node;
public:
HashTable()
:_size(0)
{
_tables.resize (_GetNextPrime(0));
}
Node* Find(const K& key)
{
if(_size==0) //为空
{
return NULL;
}
size_t index=_HashFunc(key);
Node* cur=_tables[index];
while(cur)
{
if(cur->_key ==key)
return cur; //存在
cur=cur->_next ;
}
return NULL;
}
bool Insert(const K& key,const V& value)
{
ChickCapacity();
size_t index=_HashFunc(key);
if(Find(key))
{
return false; //存在,直接返回
}
//头插
Node* tmp=new Node(key,value);
tmp->_next =_tables[index];
_tables[index]=tmp;
++_size;
return true;
}
bool Remove(const K& key)
{
if(_size==0)
return false;
size_t index=_HashFunc(key);
Node* cur=_tables[index];
Node* prev=NULL;
while(cur)
{
if(cur->_key ==key) //找的要删除的节点
{
if(prev==NULL) //第一个节点
{
_tables[index]=cur->_next ;
}
else //不是第一个节点的情况
prev->_next =cur->_next ;
delete cur;
--_size;
return true;
}
prev=cur;
cur=cur->_next ;
}
return false;
}
void Print()
{
for(size_t i=0;i<_tables.size();++i)
{
printf("_tables[%d]->",i);
Node* cur=_tables[i];
while(cur)
{
cout<<cur->_key <<"->";
cur=cur->_next ;
}
cout<<"NULL"<<endl;
}
}
~HashTable()
{
for(size_t i=0;i<_tables.size();++i)
{
Node* cur=_tables[i];
while(cur)
{
Node* next=cur->_next ;
delete cur;
cur=next;
}
}
_size=0;
_tables.clear(); //size只能减到0,不会释放空间。
//想要释放空间,需要构建一个新的空对象,
//然后交换,出了这个作用域就会自动析构掉。
}
protected:
HashTable(const HashTables& ht){}; //防拷贝
size_t _HashFunc(const K& key)
{
return key%_tables.size (); //除留余数法
}
void ChickCapacity()
{
if(_tables.size()==0||_size==_tables.size())
{
//增容
vector<Node*> tmptables;
tmptables.resize (_tables.size());
for(size_t i=0;i<_tables.size();++i)
{
Node* cur=_tables[i];
while(cur)
{
Node* next=cur->_next ;
size_t index=_HashFunc(cur->_key ); //找到新表中要的位置
//头插
cur->_next =_tables[index];
_tables[index]=cur;
cur=next;
}
}
}
}
int _GetNextPrime(size_t size)
{
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
};
for(size_t i=0;i<_PrimeSize;++i)
{
if(size<_PrimeList[i])
{
return _PrimeList[i];
}
}
}
private:
vector<Node*> _tables;
size_t _size;
};
void TestHsahTable()
{
HashTable<int,int> ht1;
int arr[]={89,18,49,58,9,111};
for(size_t i=0;i<6;++i)
{
ht1.Insert (arr[i],i);
}
ht1.Print ();
HashTable<int,int> ht2;
//ht2(ht1); //不允许拷贝
//cout<<ht1.Remove (111)<<endl; //1
//cout<<ht1.Remove (55)<<endl; //0
}