哈希表的实现

来源:互联网 发布:windows怎么装双系统 编辑:程序博客网 时间:2024/06/01 10:12
     哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
寻址方法:
1.直接寻址:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。若其中H(key)中已经有值了,就往下一个找,直到H(key)中没有值了,就放进去。
2. 除留余数法取关键值被某个不大于散列表长m的数p除后的所得的余数为散列地址。Hash(Key)= Key % P。
3. 平方取中法当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。

哈希冲突/哈希碰撞
    不同的Key值经过哈希函数Hash(Key)处理以后可能产生相同的值哈希地址,我们称这种情况为哈希冲突。任意的散列函数都不能避免产 生冲突。

   哈希表在查找过程中,关键码的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。因此,影响产生冲突多少的因素,也就是影响查找效率的因素。影响产生冲突多少有以下三个因素:
1. 散列函数是否均匀;
2. 处理冲突的方法;
3. 散列表的装填因子。
散列表的载荷因子定义为:α= 填入表中的元素个数 / 散列表的长度
α是散列表装满程度的标志因子。由于表长是定值,α与“填入表中的元素个数”成正比,所以,α越大,填入表中的元素较多,产生冲突的可能性就越大;α越小,填入表中的元素较少,产生冲突的可能性就越小。对于开放定址法,载荷因子应该严格控制在0.7-0.8之间,超过0.8时,cpu的缓存不命中率会曲线上升

#include<iostream>
#include<vector>
using namespace std;

enum Status                      //判断表中选中位置是否为空
{
 EMPTY,
 EXIST,
 DELETE
};

template<class K,class V>
struct HashNode
{
 K _key;
 V _value;
 Status _sta;

 HashNode(const K& key=K(),const V& value=V())
  :_key(key)
  ,_value(value)
  ,_sta(EMPTY)
 {}
};


template<class K,class V>
class HashTable
{
public:
 typedef HashNode<K,V> Node;

 HashTable()
  :_size(0)
 {

  _table.resize(GetNextPrime(0));
 }

 bool Insert(const K& key,const V& value)
 {

  Checksize();
  size_t index=HashFunc(key);
  while(_table[index]._sta==EXIST)
  {
   if(_table[index]._key==key)
    return false;

   ++index;
   if(index==_table.size())
    index=0;
  }
 
  
   _table[index]._key=key;
   _table[index]._value=value;
   _table[index]._sta=EXIST;
   _size++;
   return true;
   

 }


 Node* Find(const K& key)
 {

  if(_table.empty)
   return NULL;
  for(size_t i=0;i<_table.size();i++)
  {
   
      if(_table[i]._sta==EXIST && _table[i]._key==key)
   {
        return &_table[i];
   }
   else
    continue;
  }

  return NULL;
 }

 void Display()
 {
  
    if(_table.size()==0)
   return;

    for(size_t i=0;i<_table.size();i++)
  {
   cout<<_table[i]._key<<" ";
  }
  cout<< endl;
 }

protected:

 size_t HashFunc(const K& key)                 //hash函数
 {
  return key%_table.size();

 }

 void Checksize()                                      //判断存储空间
 {
  
  
     if(_table.size()==0||_size*10/_table.size()>=8)
  {

   size_t newsize=GetNextPrime(_table.size());
   HashTable hash;
   hash._table.resize(newsize);
   for(size_t i=0;i<_table.size();i++)
  {

   if(_table[i]._sta==EXIST)
   {
     hash.Insert(_table[i]._key,_table[i]._value);
   }
  }
  this->Swap(hash);
  }
   
 }

 size_t GetNextPrime(size_t num)           //增容素数数列(使用素数做除数可以减少哈希冲突),素数表对齐做哈希表的容量,降低哈希冲突
 {
  long long PrimeSize[28]={53,97,193,389,769,1543,3079,
   6151,12289,24593,49157,98317,196613,393241,786433ul,
   1572869ul,3145739,6291469,12582917,25165843,50331653,
   100663319,201326611,402653189,805306457,1610612741,
   3221225473,4294967291};
  for(size_t i=0;i<28;i++)
  {
   if(PrimeSize[i]>num)
   {
    return PrimeSize[i];
   }
   else
    continue;
  }

  return 4294967291;                              //返回最大容量约为4G

 }

 void Swap(HashTable<K,V>& hs)
 {
  _table.swap(hs._table);
  swap(_size,hs._size);
 }

private:
 vector<Node> _table;
 size_t _size;
};


#include"HashTable.h"
#include<cstdlib>
void TestHashTable()
{
 int a[40]={1,2,3,4,5,6,7,8,9,10
  ,11,12,13,14,15,16,17,18,19,20,
  21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40};
 HashTable<int,int> ht;
 for(int j=0;j<40;j++)
 {
  
  ht.Insert(a[j],j);
 }
 
 ht.Display();

 }
int main()
{
 TestHashTable();
 system("pause");
 return 0;
}


0 1
原创粉丝点击