哈希表

哈希表,又称为散列表,它是根据关键码(key)直接访问在内存中的存储的一种数据结构。它通过把关键码根据某一函数进行转换后映射到表中的,这个函数称为散列函数,存放记录的数组称为散列表。

一、哈希表的构造

如何构造一个散列表?!请看这篇博客^-^散列表的构造和哈西冲突

值得一提的是,构造这个表时,我们需要考虑这个位置的状态,是EMPTY,EXIST 还是DELETE?!当表中还没有元素时,我们给的默认状态是EMPTY。

二、哈希表实现Insert、Remove、Find(这篇文章我是用开放定址法处理哈希冲突)

Insert:

每插入一个数,就将该位置的状态改为EXIST,当插入的下一个数如果也映射在该位置,看到这个标志"EXIST"就知道这个位置已经被占用,就继续去探测下一个状态为"EMPTY"或"DELETE"的位置;如果插入的过程中发现已经有相同的key值,那么就直接返回

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //线性探测法  
  2. bool Insert(const K& key,const V& value)  
  3. {  
  4.     //先检测容量  
  5.     _CheckSize();  
  6.     //找到这个key值所在的哈希地址  
  7.   
  8.     size_t index = _HashFunc(key);  
  9.     while (_table[index]._status == EXIST)  
  10.     {  
  11.         //表中已经有关键码key  
  12.         if (_table[index]._key == key)  
  13.         {  
  14.             return false;  
  15.         }  
  16.         //如果这个位置的状态为EMPTY或DELET,直接可以将数据放入  
  17.         if (_table[index]._status == EMPTY || _table[index]._status == DELETE)  
  18.         {  
  19.             break;  
  20.         }  
  21.         ++index;  
  22.   
  23.         //如果已经到表尾也没有将数据插入,就从表头再寻找  
  24.         if (index == _table.size())  
  25.         {  
  26.             index = 0;  
  27.         }  
  28.     }  
  29.   
  30.     //index位置状态为EMPTY、DELETE  
  31.     _table[index]._key = key;  
  32.     _table[index]._value = value;  
  33.     _table[index]._status = EXIST;  
  34.     _size++;  
  35.     return true;  
  36. }  

Find:

查找数据时,也是根据关键码key来寻找的,只要这个位置的状态不为"EMPTY"时,就可以一直寻找,当某一位置的key符合条件时就返回该位置的位置;还有一种情况是找到表尾时没有找到,那么就应该先回表头继续寻找。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Node* Find(const K& key)  
  2. {  
  3.     //表有可能为空  
  4.     if (_table.size() == 0)  
  5.     {  
  6.         return false;  
  7.     }  
  8.     size_t index = _HashFunc(key);  
  9.     while (_table[index]._status != EMPTY)  
  10.     {  
  11.         if (_table[index]._key == key)  //index的内容就是要找的  
  12.         {  
  13.             return &_table[index];  
  14.         }  
  15.         ++index;  
  16.   
  17.         if (index == _table.size()) //找到表尾没有找到,就从头重新找  
  18.         {  
  19.             index = 0;  
  20.         }  
  21.     }  
  22. }  
Delete:

这里删除数据时我们采用的是“懒删除法”,即直接修改该位置的状态为"DELETE"。由于在插入数据时只关心的是该位置的状态是否为"EXIST",所以其他状态并不会对插入有影响,就算下一次插入数据的位置状态是"DELETE",直接覆盖掉就好了!!

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bool Remove(const K& key)  
  2. {  
  3.     for (size_t index=0; index<_table.size(); ++index)  
  4.     {  
  5.         if (_table[index]._key == key)  
  6.         {  
  7.             _table[index]._status = DELETE;  
  8.             return true;  
  9.         }  
  10.     }  
  11.     return false;  
  12. }  


具体代码实现:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. enum STATUS  
  2. {  
  3.     EMPTY,  
  4.     EXIST,  
  5.     DELETE,  
  6. };  
  7.   
  8. template<class K,class V>  
  9. struct KVNode  
  10. {  
  11.     K _key;  
  12.     V _value;  
  13.     STATUS _status; //标示每个位置的状态  
  14.   
  15.     KVNode(const K& key,const V& value)  
  16.         :_key(key)  
  17.         ,_value(value)  
  18.         ,_status(EMPTY)  
  19.     {}  
  20. };  
  21.   
  22. template<class K,class V>  
  23. class HashTable  
  24. {  
  25.     typedef KVNode<K,V> Node;  
  26. public:  
  27.     HashTable()  
  28.         :_size(0)  
  29.     {  
  30.         _table.resize(_GetPrimer(0));  
  31.     }  
  32.     ~HashTable()  
  33.     {  
  34.         for (size_t i=0; i<_table.size(); ++i)  
  35.         {<span style="white-space:pre">       </span>//这里应该判断这个位置的状态,如果为EMPTY就可以不用管  
  36.             _table[i]._key = 0;  
  37.             _table[i]._value = 0;  
  38.             _table[i]._status = EMPTY;  
  39.         }  
  40.     }  
  41.   
  42.     Node* Find(const K& key);  
  43.     bool Remove(const K& key);  
  44.     //线性探测法  
  45.     bool Insert(const K& key,const V& value);  
  46.   
  47.     void Display()  
  48.     {  
  49.         for (size_t i=0; i<_table.size(); ++i)  
  50.         {  
  51.             if (_table[i]._status == EXIST)  
  52.             {  
  53.                 cout<<"key->"<<_table[i]._key<<" "<<"value->"<<_table[i]._value<<endl;  
  54.             }  
  55.         }  
  56.     }  
  57. protected:  
  58.     void _CheckSize()  
  59.     {  
  60.         //当表长度为0或者负载因子大于0.8时进行增容  
  61.         //负载因子 == 插入表中的数据个数/散列表的长度  
  62.         //if (_table.size() == 0 || _size/_table.size() >= 0.8)  //size_t/size_t得不到浮点数  
  63.         if (_table.size() == 0 || _size*10/_table.size() >= 8)  
  64.         {  
  65.             size_t newSize = _GetPrimer(_table.size());  
  66.             HashTable<K,V> newHashTable;  
  67.             newHashTable._table.resize(newSize);  
  68.             //将原表中的数据插入到新表中,并在插入时重新计算哈希地址----借助Insert  
  69.             for (size_t i=0; i<_table.size(); ++i)  
  70.             {  
  71.                 if (_table[i]._status == EXIST)  
  72.                 {  
  73.                     newHashTable.Insert(_table[i]._key,i);  
  74.                 }  
  75.             }  
  76.             swap(this->_table,newHashTable._table);  
  77.             swap(_size,newHashTable._size);  
  78.         }  
  79.     }  
  80.     //利用素数表来获取_table的容量----选素数,会降低冲突  
  81.     size_t _GetPrimer(size_t size)  
  82.     {  
  83.         const int _PrimeSize = 28;  
  84.         static const unsigned long _PrimeList[_PrimeSize] =  
  85.         {  
  86.             53ul, 97ul, 193ul, 389ul, 769ul,  
  87.             1543ul, 3079ul, 6151ul, 12289ul, 24593ul,  
  88.             49157ul, 98317ul, 196613ul, 393241ul,  
  89.             786433ul,  
  90.             1572869ul, 3145739ul, 6291469ul, 12582917ul,  
  91.             25165843ul,  
  92.             50331653ul, 100663319ul, 201326611ul, 402653189ul,  
  93.             805306457ul,  
  94.             1610612741ul, 3221225473ul, 4294967291ul  
  95.         };  
  96.         for (size_t i=0; i<_PrimeSize; ++i)  
  97.         {  
  98.             if (_PrimeList[i] > size)  
  99.             {  
  100.                 return _PrimeList[i];  
  101.             }  
  102.         }  
  103.     }  
  104.     size_t _HashFunc(const K& key)  //散列函数  
  105.     {  
  106.         return key%_table.size();  
  107.     }  
  108. protected:  
  109.     vector<Node> _table;  
  110.     size_t _size;       //表中的数据个数  
  111. };  


ps:下篇文章会实现开链法解决哈希冲突!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值