HahTable——拉鍊法實現的雜湊表
阿新 • • 發佈:2019-02-18
由於雜湊表的查詢高效性,在平時的演算法中用的也是比較多。例如:字串、單詞個數的統計,只出現一次字元或者數字的統計,兩個集合相同元素的查詢等等,還有插入刪除的高效(鏈地址法)都可以用雜湊表來解決。缺點可能是需要佔用額外的記憶體空間。
1 原理分析
處理雜湊衝突的另一種方法是拉鍊法,也稱為雜湊桶。適用於經常進行插入和刪除的情況。一方法參考:https://blog.csdn.net/qq_15000103/article/details/80270964
拉鍊法解決衝突的做法是:將所有關鍵字為同義詞的結點連結在同一個單鏈表中。若選定的散列表長度為m,則可將散列表定義為一個由m個頭指標組成的指標陣列t[0..m-1]。凡是雜湊地址為i的結點,均插入到以t為頭指標的單鏈表中。t中各分量的初值均應為空指標。在拉鍊法中,裝填因子α可以大於1,但一般均取α≤1。
2 程式碼實現
//HashTable.h
#pragma once
#include<vector>
#include<utility>
#include<string>
template<class V>
struct HashNode
{
V _v;//set->k;map->kv
HashNode<V>* _next;
HashNode(const V& v)
:_v(v)
, _next(NULL)
{}
};
template<class K>
struct Hash
{
size_t operator()(const K& key)
{
return key;
}
};
template<>
struct Hash<string>//特化,使得預設時,若調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& key)
{
return BKDRHash(key.c_str());
}
};
template<class K, class V, class KeyOfValue, class _HashFunc>//unordered_set<K,V>->HashTable<K,K>;unordered_map<K,V>->HashTable<K,pair<K,V>>
class HashTable;
template<class K, class V, class KeyOfValue, class _HashFunc>
struct _HashTableIterator//單向迭代器
{
typedef HashNode<V> Node;
typedef _HashTableIterator<K, V, KeyOfValue,_HashFunc> Self;
typedef HashTable<K, V, KeyOfValue, _HashFunc> HT;
Node* _node;
HashTable<K, V, KeyOfValue, _HashFunc>* _ht;
_HashTableIterator(Node* node, HT* ht)
:_node(node)
, _ht(ht)
{}
V& operator*()
{
return _node->_v;
}
V* operator->()
{
return &(operator*());
}
Self& operator++()
{
if (_node->_next)
{
_node = _node->_next;
}
else
{
KeyOfValue kov;
size_t index = _ht->HashFunc(kov(_node->_v), _ht->_table.size());
++index;
while (index < _ht->_table.size())
{
if (_ht->_table[index])
{
_node = _ht->_table[index];
break;
}
else
{
++index;
}
}
if (index == _ht->_table.size())
{
_node = NULL;
}
}
return *this;
}
Self operator++(int)
{
Self tmp(*this);
++*this;
return tmp;
}
bool operator==(const Self& s )
{
return _node == s._node;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
};
template<class K, class V,class KeyOfValue,class _HashFunc=Hash<K>>//unordered_set<K,V>->HashTable<K,K>;unordered_map<K,V>->HashTable<K,pair<K,V>>
class HashTable
{
typedef HashNode<V> Node;
friend struct _HashTableIterator<K, V, KeyOfValue,_HashFunc>;
public:
typedef _HashTableIterator<K, V, KeyOfValue, _HashFunc> Iterator;
Iterator Begin()
{
for (size_t i = 0; i < _table.size(); i++)
{
if (_table[i])
{
return Iterator(_table[i], this);
}
}
return End();
}
Iterator End()
{
return Iterator(NULL, this);
}
HashTable()
: _size(0)
{}
bool Insert(const V& v)
{
CheckCapacity();
KeyOfValue kov;
size_t index = HashFunc(kov(v), _table.size());
Node* cur = _table[index];
while (cur)
{
if (kov(cur->_v) == kov(v))
return false;
cur = cur->_next;
}
Node* node = new Node(v);
//頭插
node->_next = _table[index];
_table[index] = node;
++_size;
return true;
}
Node* Find(const K& key)
{
size_t index = HashFunc(key);
Node* cur = _table[index];
KeyOfValue kov;
while (cur)
{
if (kov(cur->_v) == key)
{
return cur;
}
cur = cur->_next;
}
return NULL;
}
bool Remove(const K& key)
{
KeyOfValue kov;
size_t index = HashFunc(key);
Node* cur = _table[index];
if (cur == NULL)
{
return false;
}
if (kov(cur->_v) == key)//頭刪
{
_table[index] = cur->_next;
delete cur;
return true;
}
else//中間刪
{
Node* prev = cur;
cur = cur->_next;
while (cur)
{
if (kov(cur->_v) == key)
{
prev->_next = cur->_next;
delete cur;
return true;
}
prev = cur;
cur = cur->_next;
}
}
return true;
}
size_t HashFunc(const K& key, size_t size)
{
_HashFunc hf;
return hf(key) % size;
}
size_t GetNextPrimeNum(size_t num)
{
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 (_PrimeList[i] > num)
{
return _PrimeList[i];
}
}
return _PrimeList[_PrimeSize - 1];
}
void CheckCapacity()
{
if (_table.size() == 0)
{
_table.resize(GetNextPrimeNum(0), NULL);
}
else if (_size == _table.size())//考慮負載因子
{
size_t newsize = GetNextPrimeNum(_table.size());
if (newsize = _table.size())
{
return;
}
vector<Node*> newtable;
newtable.resize(newsize, NULL);
KeyOfValue kov;
for (size_t i = 0; i < _table.size(); ++i)
{
Node* cur = _table[i];
while (cur)
{
size_t index = HashFunc(kov(cur->_v), newsize);
Node* next = cur->_next;
//頭插到newtable;
cur->_next = newtable[index];//將cur指向新表的位置
newtable[index] = cur;
cur = next;
}
_table[i] = NULL;
}
_table.swap(newtable);
}
}
private:
std::vector<Node*> _table;
size_t _size;
};
template<class K>
struct SetKeyOfValue
{
const K&operator()(const K& key)
{
return key;
}
};
template<class K,class V>
struct MapKeyOfValue
{
const K&operator()(const std::pair<K,V>& kv)
{
return kv.first;
}
};
測試程式碼:
//test.cpp
#include<iostream>
#include<utility>
#include"HashTable.h"
#include<string>
using namespace std;
void TestHashTable()
{
typedef HashTable<int, int, SetKeyOfValue<int>> IntSet;
typedef HashTable<int, int, SetKeyOfValue<int>>::Iterator IntSetIter;
IntSet ht;
ht.Insert(4);
ht.Insert(14);
ht.Insert(24);
ht.Insert(33);
ht.Insert(55);
ht.Insert(19);
ht.Insert(55);
IntSetIter it = ht.Begin();
while (it != ht.End())
{
cout << *it << " ";
++it;
}
cout << endl;
typedef HashTable<string, string, SetKeyOfValue<string>> StringSet;
typedef HashTable<string, string, SetKeyOfValue<string>>::Iterator StringSetIter;
StringSet ss;
ss.Insert("sort");
ss.Insert("left");
ss.Insert("string");
ss.Insert("char");
ss.Insert("char");
StringSetIter it1 = ss.Begin();
while (it1!=ss.End())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
}
int main()
{
TestHashTable();
system("pause");
return 0;
}
執行結果如下: