1.哈希表的提出—为什么用哈希
我们已经学过了很多的数据结构,这次我们来探究一下一种新的数据结构,这种数据结构叫做哈希表,这种数据结构的特点是查询效率特别高,最高可达到O(1),这也是这种数据结构被提出的原因。
什么是哈希冲突?哈希冲突怎么解决
哈希冲突概念:
但关键字集合很大时,关键字值不同的元素可能会映射到哈希表的同一个地址上,即k1不等于k2,可是H(k1)=H(k2),把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”,由同义词引起的哈希冲突称为同义词冲突。事实上哈希冲突是不可避免的,由于关键字可能发生冲突的集合远远大于实际开辟的哈希表长度,构成了冲突的必然性,可通过改进哈希函数的性能来减少冲突,即降低冲突的可能性。
如何设计一个好的哈希函数,需要注意什么问题?常见的哈希函数有哪些?
构造哈希函数要注意以下几点:
(1)哈希函数的定义域必须包括需要存储的全部关键码,如果散列表允许有m个地址时,其值域必须在0和m-1之间
(2)哈希函数所能计算出来的地址能均匀分布在整个空间中
(3)哈希函数应该比较简单
常见的哈希函数:
(1)直接定址法
去关键字的某个线性函数为散列地址:Hash(key) = A*key+B;
优点:简单、均匀
缺点:需要事先知道关键字的分布情况
适用范围:适合查找比较小且连续的情况
(2)除留余数法
设散列中允许的地址数为m,取一个不大于m,或者最接近或者等于m的质数p作为除数,按照哈希函数Hash(key) = key%p p<=m;将关键码转换为哈希地址
(3)平方取中法
设关键字是1234,那么它的平方就是1522756,在抽取中间的3位就是227,作为三列地址;再比如关键码是4321,那么它的平方就是18671041,抽取中间的三位就可以是671或者710用作散列地址。平方取中法比较合适:不知道关键字,的分布,而位数又不是很多的情况
(4)折叠法
折叠法是将关键字从左到右分割为位数(注意:最后一部分位数不够时可以短些),然后将这几部分叠加求和,并按散列表表长,取后几位为散列地址,比如:关键字是:9876543210,散列表表长为三位,我们将它分为四组987|654|321|0|,然后将它们叠加求和:987+654+321+0=1962,再求后三位得到散列地址为962.有时可能这还不能保证分布均匀,不妨从一端向另一端来回折叠后对齐相加。比如将987和321反转,再与654和0相加,变成789+654+321+0=1566,此时的散列地址为566。折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况
(5)随机数法
选择一个随机函数,取关键字的随机哈希函数值作为哈希地址,即H(key)=random(key),其中random是随机数函数通常用于关键字长度不等情况
(6)数学分析法
设有n个d位数,每一位可能有r种不同的符号,这r种不同的符号在各位上出现的频率不一定相同可能某些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀,只有某几种符号经常出现。可根据散列表的大小,选择其中各种符号分布均匀的若干位作为三列地址。
假设要存储某家公司员工登记表,如果用手机号作为关键字,那么极有可能前7位都是相同的,那么我们可以选择后面的四位作为散列地址,如果这样的抽取工作还容易出现冲突 ,还可以对抽取出来的数字进行反装(1234->4321)、右环位移(1234->4123)、左环移位、前两数与后两数相加叠加(1234->12+34=46)等方法,总之:为了提供一个散列函数,能够合理的将关键字分配到散列表的各位置。数学分析法常适合处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布比较均匀的情况
哈希冲突的解决方式:
任何一种函数也不可能避免产生冲突,因此选择好的解决冲突溢出的方法非常重要。为了减少冲突,必须对散列表加以改造。
1. 闭散列(开放定址法)—从发生哈希冲突位置开始,找下一个空余位置
具体细节:
闭散列法也叫开地址法。设散列表的编码是从0-m-1,当添加关键码key时通过散列函数hash(key)计算key的存放位置,但是存放时发现这个桶已经被另一个keyx占据了,即发生哈希冲突,如果表未被装满,表示在给定的范围内必然还有空位置,则可以把key存放在表中“下一个”空位中。
简而言之,就是,一旦发生冲突,就去寻找下一个空的散列表的地址,只要散列表足够大,空的散列地址总能找的到。找空桶的方法如下:
a. 线性探测—-逐次往后找——优缺点
设给出一组元素 ,它们的关键码为:37,25,14,36,49,68,57,11,散列表为HT[12],标的大小m=12,假设采用Hash(x)=x%p;//(p=11)11是最接近m的质数,就有:
Hash(37)=4 Hash(25)=3 Hash(14)=3 Hash(36)=3
Hash(49)=5 Hash(68)=2 Hash(57)=2 Hash(11)=0
使用线性探测法处理冲突:
添加元素时,使用散列函数确定元素的插入位置,如果此空间有值:
1、该值是所要插入元素的关键码,不进行插入
2、产生冲突,依次查看其后的下一个桶,如果发现空位置插入新元素
在闭散列的情形下不能随便物理删除表中已有的元素。因为删除元素会影响其他元素的搜索。
散列表的载荷因子定义为:a=填入表中的元素个数/散列表的长度
a是散列表装满程度的标志因子。由于表长是定值,a与“填入表中的元素个数”成正比,所以a元素越多,产生冲突的可能性就越大:反之,a越小,表明填入表中的元素越少,产生冲突的可能性越小,表的平均长度是载荷因子a的函数,只是不同处理冲突的方法有不同的函数
对于开放定址法,载荷因子是特别重要元素,应严格限制在0.7-0.8以下。超过0.8,查表时cpu的缓存按照指数曲线上升,因此一些采用开放定址法的hash库,如Java的系统库限制了载荷因子resize散列表
优点:有效减少了哈希冲突的产生
缺点:线性探查法容易产生“数据堆积”,即不同探查序列的关键码占据了可利用的空位置,使得寻找某关键码的位置需要多次比较,导致搜索时间加快
b. 二次探测—-平方向后探测—–优缺点
使用二次探测法,在表中寻找“下一个”空位置的公式为:
Hi=(H0+i^2)%m,Hi=(H0-i^2)%m,i=1,2,3,….,(m-1)/2
H0是通过散列函数Hash(x)对元素的关键码x进行计算得到的位置,m是表的大小
假设数组的关键码为37,25,14,36,49,68,57,11,假设取m=19,这样可设定为HT[19],采用散列函数,Hash(x)=x%19
Hash(37)=18 Hash(25)=6 Hash(14)=14 Hash(36)=17
Hash(49)=11 Hash(68)=11 Hash(57)=0 Hash(11)=11
研究表明当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且一个位置都不会被探查两次。因此只要有表中有一半的空的,就不会有表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5;如果超出必须考虑增容。
c、双散列法
使用双散列法,需要两个散列函数。第一个散列函数Hash()按关键码key计算其所在的位置H0=Hash(key)。一旦发生冲突,利用两个第二个散列函数ReHash()计算该key到达“下一个”桶的位移,它的取值与key的值有关,要求它的取值应该是小于地址空间大小TableSize,且与TableSize互质的正整数。
设表的长度为m=TableSize,则在表中寻找“下一个”桶的公式为:
j=H0=Hash(key),p=ReHash(key);j=(j+p)%m;p是小于m且与m互质的整数
闭散列的缺陷
当插入的数据较多时,查询的效率就会降低
2. 开散列(拉链发–开链法)
a. 开链法如何解决哈希冲突?
把具有相同地址的关键码归于一个子集,每个子集称为一个桶,每个桶中的元素用单链表进行连接,各链表的头结点组成一个向量。
b. 开链法什么情况下增容
在每个链上都链有一个数据,负载因子为一就可以进行增容了。
c. 开链法如果受到恶意攻击,效率会急剧下降,如何解决
进行扩容,或者在链表下面加一个红黑树。
d. 尝试给开链法增加迭代器
e. 开链法的应用—封装
1 完成闭散列要求:
插入元素唯一
写成动态的考虑如何增容?
哈希函数用出留余数法,考虑余数每次模素数 增容尽量增加到原空间的两倍
哈希表中任意类型都可以存储
代码实现如下:
哈希表头文件:
开散列:
静态版本:
hashtable.h
#pragma once
#include<vector>
#define MAXSIZE 10
typedef enum State
{EMPTY,EXITS,DELETE};
template<class K>
struct Elem
{
K _key;
State _state;
};
template<class K,bool Isline = true>
class Hashtable
{
public:
Hashtable()
:_size(0)
{
for (size_t i = 0; i < MAXSIZE; i++)
{
_hashtable[i]._state = EMPTY;
}
}
bool Insert(const K& key)
{
size_t hashAddr = HashFunc(key);
if (_size * 10 / MAXSIZE>7)
return false;
size_t i = 1;
while (_hashtable[hashAddr]._state != EMPTY)
{
if (_hashtable[hashAddr]._state == EXITS&&key == _hashtable[hashAddr]._key)
return false;
if (Isline)
LineCheck(hashAddr);
else
SecondCheck(hashAddr, i++);
}
_hashtable[hashAddr]._state = EXITS;
_hashtable[hashAddr]._key = key;
_size++;
return true;
}
int Find(const K& key)
{
size_t hashAddr = HashFunc(key);
size_t startAddr = hashAddr;
int i = 1;
while (EMPTY != _hashtable[hashAddr]._state)
{
if (_hashtable[hashAddr]._state == EXITS)
{
if (_hashtable[hashAddr]._key == key)
return hashAddr;
}
hashAddr++;
if (hashAddr == startAddr)
return -1;
if (hashAddr == MAXSIZE)
hashAddr = 0;
if (Isline)
LineCheck(hashAddr);
else
SecondCheck(hashAddr, i++);
}
return -1;
}
bool Delete(const K& key)
{
int ret = Find(key);
if (ret != -1)
{
_hashtable[ret]._state = DELETE;
_size--;
return true;
}
return false;
}
size_t Size()const
{
return _size;
}
bool Empty()
{
return 0 == _size;
}
~Hashtable()
{}
private:
size_t HashFunc(const K& key)
{
return key%MAXSIZE;
}
void LineCheck(size_t& hashAddr)
{
++hashAddr;
if (hashAddr >= MAXSIZE)
hashAddr = 0;
}
void SecondCheck(size_t& hashAddr, size_t i)
{
hashAddr = hashAddr + ((i << 1) + 1);
if (hashAddr >= MAXSIZE)
hashAddr %= MAXSIZE;
}
size_t _size;
Elem<K> _hashtable[MAXSIZE];
};
#include<iostream>
using namespace std;
void HashTableTest()
{
Hashtable<int,false> ht;
ht.Insert(23);
ht.Insert(3);
ht.Insert(53);
ht.Insert(26);
ht.Insert(32);
ht.Insert(12);
cout << ht.Size() << endl;
ht.Delete(32);
ht.Delete(53);
cout << ht.Size() << endl;
}
动态版本:
hashtable.h
#pragma once
#include"comm.h"
#include<string>
#include<iostream>
using namespace std;
typedef enum STATE{ EMPTY, EXIST, DELETE };
template<class K>
struct Elem
{
STATE _state;
K _key;
};
template<class K, class KeyToInt=KeyToIntDef<int>, bool IsLine=true>
class HashTable
{
public:
HashTable(size_t capacity)
{
capacity = GetNextPrimer(capacity);
_hashtable = new Elem<K>[capacity];
_capacity = capacity;
for (size_t i = 0; i < capacity; i++)
{
_hashtable[i]._state = EMPTY;
}
_size = 0;
}
bool Insert(const K& key)
{
CheckCapacity();
size_t hashaddr = HashFunc(key);
size_t i = 1;
while (_hashtable[hashaddr]._state != EMPTY)
{
if (_hashtable[hashaddr]._state == EXIST && _hashtable[hashaddr]._key == key)
return false;
if (IsLine)
LineCheck(hashaddr);
else
SecondCheck(hashaddr, i++);
}
_hashtable[hashaddr]._state = EXIST;
_hashtable[hashaddr]._key = key;
_size++;
return true;
}
int Find(const K& key)
{
size_t hashaddr = HashFunc(key);
size_t i = 1;
while (EMPTY != _hashtable[hashaddr]._state)
{
if (_hashtable[hashaddr]._key == key)
return hashaddr;
if (IsLine)
LineCheck(hashaddr);
else
SecondCheck(hashaddr, i++);
}
return -1;
}
bool Delete(const K& key)
{
int ret = Find(key);
if (-1!=ret)
{
_hashtable[ret]._state = DELETE;
_size--;
return true;
}
return false;
}
size_t Size()
{
return _size;
}
size_t Capacity()
{
return _capacity;
}
bool Empty()
{
return (_size == 0);
}
~HashTable()
{
if (_hashtable)
{
delete[] _hashtable;
_size = 0;
_capacity = 0;
}
}
private:
size_t HashFunc(const K& key)
{
return KeyToInt()(key) % _capacity;
}
void LineCheck(size_t &hashaddr)
{
++hashaddr;
if (hashaddr > _capacity)
hashaddr = 0;
}
void SecondCheck(size_t& hashaddr, size_t i)
{
hashaddr = hashaddr+((i << 1)+1);
if (hashaddr >= _capacity)
{
hashaddr %= _capacity;
}
}
void CheckCapacity()
{
if (_size * 10 / _capacity > 7)
{
size_t capacity = GetNextPrimer(_capacity);
HashTable<K, KeyToInt, IsLine> newht(capacity);
for (size_t i = 0; i < _capacity; i++)
{
if (_hashtable[i]._state == EXIST)
{
newht.Insert(_hashtable[i]._key);
}
}
Swap(newht);
}
}
void Swap(HashTable<K, KeyToInt, IsLine> ht)
{
swap(_hashtable, ht._hashtable);
swap(_size, ht._size);
swap(_capacity, ht._capacity);
}
private:
Elem<K> *_hashtable;
size_t _size;
size_t _capacity;
};
void HashTableTest()
{
HashTable<string,StringToIntDef> ht1(10);
ht1.Insert("111111");
ht1.Insert("222222222");
ht1.Insert("3333333");
ht1.Insert("666666666");
ht1.Insert("你好");
if (-1 != ht1.Find("你好"))
{
cout << "Is in hashtable" << endl;
}
else
cout << "Is not in hashtable" << endl;
HashTable<int> ht2(10);
ht2.Insert(12);
ht2.Insert(20);
ht2.Insert(32);
ht2.Insert(200);
ht2.Insert(66);
if (ht2.Find(200) != -1)
cout << "Is in hashtable!\n" << endl;
else
cout << "Is not in hashtable\n" << endl;
cout << ht1.Capacity() << endl;
cout << ht2.Capacity() << endl;
cout << ht1.Size() << endl;
cout << ht2.Size() << endl;
ht2.Delete(200);
ht1.Delete("你好");
cout << ht1.Size() << endl;
cout << ht2.Size() << endl;
}
字符串转数字,及容量取值(使用素数可有效减少哈希冲突)
comm.h
#pragma once
//使用素数作哈希表的容量能有效降低哈希冲突
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
};
size_t GetNextPrimer(size_t num)
{
for (size_t i = 0; i < _PrimeSize; i++)
{
if (_PrimeList[i]>num)
{
return _PrimeList[i];
}
}
return _PrimeList[_PrimeSize - 1];
}
static size_t BKDRHash(const char* str)
{
unsigned int seed = 131;
unsigned int hash = 0;
while (*str)
{
hash = hash*seed + (*str++);
}
return (hash&0x7FFFFFFF);
}
#include<string>
template<class K>
class KeyToIntDef
{
public:
size_t operator()(const K& key)
{
return key;
}
};
class StringToIntDef
{
public:
size_t operator()(const std::string & key)
{
return BKDRHash(key.c_str());
}
};
class KeyToInt1
{
public:
size_t operator()(const std::string & s)
{
return SDBMHash(s.c_str());
}
private:
size_t SDBMHash(const char* str)
{
register size_t hash = 0;
while (size_t ch = (size_t)*str++)
{
hash = 6559 * hash + ch;
}
return hash;
}
};
class KeyToInt2
{
public:
size_t operator()(const std::string &s)
{
return RSHash(s.c_str());
}
private:
size_t RSHash(const char* str)
{
register size_t hash = 0;
size_t magic = 63689;
while (size_t ch = (size_t)*str++)
{
hash = hash*magic + ch;
magic *= 378551;
}
return hash;
}
};
class KetToInt3
{
public:
private:
size_t APHash(const char* str)
{
register size_t hash = 0;
size_t ch;
for (size_t i = 0; ch = (size_t)*str++; i++)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
}
else
{
hash ^= ~((hash << 11) ^ ch ^ (hash >> 5));
}
}
return hash;
}
};
class KeyToInt4
{
public:
size_t operator()(const std::string s)
{
return JSHash(s.c_str());
}
private:
size_t JSHash(const char* str)
{
if (!*str)
return 0;
register size_t hash = 1315423911;
while (size_t ch = (size_t)*str++)
{
hash ^= ((hash << 5) ^ ch ^ (hash >> 2));
}
return hash;
}
};
class KeyToInt5
{
public:
size_t operator()(const std::string s)
{
return DEKHash(s.c_str());
}
private:
size_t DEKHash(const char* str)
{
if (!*str)
return 0;
register size_t hash = 1315423911;
while (size_t ch = (size_t)*str++)
{
hash ^= (hash << 5) ^ ch ^ (hash >> 2);
}
return hash;
}
};
闭散列头文件:(加迭代器)
#pragma once
#include<iostream>
#include<vector>
#include"comm.h"
using namespace std;
template<class K, class V>
struct HashTableNode
{
HashTableNode<K, V>* pNext;
K _key;
V _value;
HashTableNode(const K& key, const V& value)
:pNext(NULL),
_key(key),
_value(value)
{
}
};
template<class K, class V, class KeyToInt>
class HashTable;
template<class K, class V, class KeyToInt>
class HashTableIterator
{
typedef HashTableNode<K, V> Node;
typedef Node* pNode;
typedef HashTableIterator<K, V, KeyToInt> Self;
public:
HashTableIterator()
:_pCur(NULL),
_ht(NULL)
{}
HashTableIterator(pNode pCur, HashTable<K, V, KeyToInt>* ht)
:_pCur(pCur),
_ht(ht)
{}
HashTableIterator(const Self& ht)
{
_pCur = ht._pCur;
_ht = ht._ht;
}
pair<K, V> operator*()
{
return make_pair(_pCur->_key, _pCur->_value);
}
pair<K, V>* operator->()
{
return &(operator*());
}
Self& operator++()
{
Next();
return *this;
}
Self operator++(int)
{
Self temp(*this);
Next();
return temp;
}
bool operator==(const Self& s)const
{
return (_pCur == s._pCur) && (_ht == s._ht);
}
bool operator!=(const Self& s)const
{
return !(s == *this);
}
private:
void Next()
{
if (_pCur->pNext)
_pCur = _pCur->pNext;
else
{
size_t BucketNo = _ht->HashFunc(_pCur->_key) + 1;
for (; BucketNo < _ht->_hashtable.capacity(); ++BucketNo)
{
if (_ht->_hashtable[BucketNo])
{
_pCur = _ht->_hashtable[BucketNo];
return;
}
}
_pCur = NULL;
}
}
pNode _pCur;
HashTable<K, V, KeyToInt>* _ht;
};
template<class K, class V, class KeyToInt=KeyToIntDef<int>>
class HashTable
{
typedef HashTableNode<K, V> Node;
typedef Node* pNode;
friend HashTableIterator<K, V, KeyToInt>;
public:
typedef HashTableIterator<K, V, KeyToInt> Iterator;
Iterator Begin()
{
for (size_t i = 0; i < _hashtable.capacity(); i++)
{
if (_hashtable[i])
{
return Iterator(_hashtable[i], this);
}
}
return Iterator(NULL, this);
}
Iterator End()
{
return Iterator(NULL, this);
}
HashTable(size_t capacity = 10)
{
capacity = GetNextPrimer(capacity);
_hashtable.resize(capacity);
_size = 0;
}
pair<Iterator, bool> InsertEqual(const K& key, const V& value)
{
size_t BucketNo = HashFunc(key);
pNode NewNode = new Node(key, value);
NewNode->pNext = _hashtable[BucketNo];
_hashtable[BucketNo] = NewNode;
_size++;
return pair<Iterator, bool>(Iterator(_hashtable[BucketNo], this),true);
}
pair<Iterator, bool> InsertUnique(const K& key, const V& value)
{
size_t BucketNo = HashFunc(key);
pNode pCur = _hashtable[BucketNo];
while (pCur)
{
if (pCur->_key == key)
return make_pair(Iterator(pCur, this), false);
pCur = pCur->pNext;
}
pCur = new Node(key, value);
pCur->pNext = _hashtable[BucketNo];
_hashtable[BucketNo] = pCur;
_size++;
return make_pair(Iterator(pCur, this), true);
}
bool DeleteEqual(const K& key)
{
size_t BucketNo = HashFunc(key);
pNode pCur = _hashtable[BucketNo];
pNode pPre = NULL;
size_t oldsize = _size;
while (pCur)
{
if (pCur->_key == key)
{
if (pCur == _hashtable[BucketNo])
{
_hashtable[BucketNo] = pCur->pNext;
delete pCur;
pCur = _hashtable[BucketNo];
}
else
{
pPre->pNext = pCur->pNext;
delete pCur;
pCur = pPre->pNext;
}
_size--;
}
else
{
pPre = pCur;
pCur = pCur->pNext;
}
}
return oldsize != _size;
}
bool DeleteUnique(const K& key)
{
size_t BucketNo = HashFunc(key);
pNode pCur = _hashtable[BucketNo];
pNode pPre = NULL;
while (pCur)
{
if (pCur->_key == key)
{
if (pCur == _hashtable[BucketNo])
_hashtable[BucketNo] = pCur->pNext;
else
pPre->pNext = pCur->pNext;
delete pCur;
_size--;
return true;
}
else
{
pPre = pCur;
pCur = pCur->pNext;
}
}
return false;
}
bool Empty()
{
return 0 == _size;
}
size_t Size()
{
return _size;
}
size_t Capacity()
{
return _hashtable.capacity();
}
void Swap(HashTable<K, V> &ht)
{
swap(_hashtable, ht._hashtable);
swap(_size, ht._size);
}
void Clear()
{
for (size_t BucketNo = 0; BucketNo < _hashtable.capacity(); ++BucketNo)
{
pNode pCur = _hashtable[BucketNo];
while (pCur)
{
_hashtable[BucketNo] = pCur->pNext;
delete pCur;
--_size;
}
}
}
Iterator Find(const K& key)
{
size_t BucketNo = HashFunc(key);
pNode pCur = _hashtable[BucketNo];
while (pCur)
{
if (pCur->_key == key)
return Iterator(pCur, this);
else
pCur = pCur->pNext;
}
return Iterator(NULL, this);
}
size_t Count(const K& key)
{
size_t BucketNo = HashFunc(key);
pNode pCur = _hashtable[BucketNo];
size_t count = 0;
while (pCur)
{
if (pCur->_key == key)
count++;
pCur = pCur->pNext;
}
return count;
}
size_t BucketCount()
{
return _hashtable.capacity();
}
size_t BucketCount(size_t BucketNo)
{
pNode pCur = _hashtable[BucketCount];
while (pCur)
{
count++;
pCur = pCur->pNext;
}
return count;
}
V FindORInsert(const K& key)
{
size_t BucketNo = HashFunc(key);
pNode pCur = _hashtable[BucketNo];
while (pCur)
{
if (key == pCur->_key)
return pCur->_value;
pCur = pCur->pNext;
}
pair<Iterator, bool> temp = InsertEqual(key, V());
return (*(temp.first)).second;
}
size_t BucketSize(size_t BucketNo)
{
size_t size = 0;
pNode pCur = _hashtable[BucketNo];
while (pCur)
{
size++;
pCur = pCur->pNext;
}
return size;
}
~HashTable()
{
Clear();
}
private:
size_t HashFunc(const K& key)
{
return KeyToInt()(key) % _hashtable.capacity();
}
void CheckCapacity()
{
size_t capacity = _hashtable.capacity();
if (_size == capacity)
{
HashTable<K, V> newHT(GetNextPrimer(capacity));
for (size_t BucketNo = 0; BucketNo < _hashtable.capacity(); ++BucketCount)
{
pNode pCur = _hashtable[BucketCount];
while (pCur)
{
newHT.InsertEqual(pCur->_key, pCur->_value);
pCur = pCur->pNext;
}
}
Swap(newHT);
}
}
std::vector<pNode> _hashtable;
size_t _size;
};
void HashBucketTest()
{
HashTable<int, int> ht(10);
ht.InsertUnique(10, 10);
ht.InsertUnique(20, 20);
ht.InsertUnique(23, 23);
ht.InsertUnique(13, 13);
ht.InsertUnique(36, 36);
ht.InsertUnique(18, 18);
ht.InsertUnique(18, 18);
ht.InsertUnique(78, 78);
/*ht.InsertEqual(78, 78);
ht.InsertEqual(10, 10);
ht.InsertEqual(20, 20);
ht.InsertEqual(23, 23);
ht.InsertEqual(13, 13);
ht.InsertEqual(36, 36);
ht.InsertEqual(18, 18);
ht.InsertEqual(18, 18);
ht.InsertEqual(78, 78);
cout << ht.Capacity() << endl;
cout << ht.Size() << endl;*/
HashTable<int, int>::Iterator it = ht.Begin();
while (it != ht.End())
{
cout << it->first << " ";
++it;
}
cout << endl;
cout << ht.BucketCount() << endl;
cout << ht.Count(23) << endl;
cout << ht.FindORInsert(89) << endl;
cout << ht.FindORInsert(23) << endl;
}
对Hash表的封装
UnorderMap.h
#pragma once
#include"HashBucket.h"
template <class K, class V, class KeyToInt = KeyToIntDef<int>>
class UnorderMap
{
public:
typename typedef HashTable<K, V, KeyToInt>::Iterator Iterator;
public:
UnorderMap()
:_ht()
{}
Iterator Begin()
{
return _ht.Begin();
}
Iterator End()
{
return _ht.End();
}
pair<Iterator, bool> Insert(const K& key, const V& value)
{
return _ht.InsertUnique(key, value);
}
pair<Iterator, bool> Erase(const K& key)
{
return _ht.DeleteUnique();
}
size_t Size()
{
return _ht.Size();
}
bool Empty()
{
return _ht.Empty();
}
size_t Capacity()
{
_ht.Capacity();
}
void Clear()
{
_ht.Clear();
}
Iterator Find(const K& key)
{
return _ht.Find(key);
}
size_t Count(const K& key)
{
return _ht.Count();
}
V operator[](const K& key)
{
return _ht.FindORInsert(key);
}
size_t BucketCount()
{
return _ht.BucketCount();
}
size_t BucketSize(size_t BucketNo)
{
return _ht.BucketSize(BucketNo);
}
private:
HashTable<K, V, KeyToInt> _ht;
};
#include<string>
using namespace std;
void UnorderMapTest()
{
UnorderMap<string, string, StringToIntDef> map;
map.Insert("1111", "1111");
map.Insert("2222", "2222");
map.Insert("3333", "3333");
map.Insert("4444", "4444");
map.Insert("1111", "4444");
cout << map["4444"] << endl;
cout << map["5555"] << endl;
UnorderMap<string, string, StringToIntDef>::Iterator it = map.Begin();
while (it != map.End())
{
cout << (*it).first << "--->" << (*it).second << endl;
++it;
}
cout << endl;
cout << "unorderedMAp中桶的个数:" << map.BucketCount() << endl;
cout << "5号桶中元素的个数:" << map.BucketSize(5) << endl;
}
UnorderSet.h
#pragma once
#include"HashBucket.h"
template<class K, class KeyToInt = KeyToIntDef<int>>
class UnorderSet
{
public:
typename typedef HashTable<K, K, KeyToInt>::Iterator Iterator;
UnorderSet()
:_ht()
{}
Iterator Begin()
{
return _ht.Begin();
}
Iterator End()
{
return _ht.End();
}
pair<Iterator, bool> Insert(const K& key, const K& value)
{
return _ht.InsertUnique(key, value);
}
pair<Iterator, bool> Erase(const K& key)
{
return _ht.DeleteUnique(key);
}
size_t Size()const
{
return _ht.Size();
}
size_t Capacity()const
{
return _ht.Capacity();
}
size_t Count(const K& key)
{
return _ht.Count(key);
}
bool Empty()
{
return _ht.Empty();
}
void clear()
{
_ht.Clear();
}
Iterator Find(const K& key)
{
return _ht.Find(key);
}
K operator[](const K& key)
{
return _ht.FindORInsert(key);
}
size_t BucketCount()
{
return _ht.BucketCount();
}
size_t BucketSize(size_t BucketNo)
{
return _ht.BucketSize(BucketNo);
}
private:
HashTable<K,K, KeyToInt> _ht;
};
#include<string>
using namespace std;
void UnorderSetTest()
{
UnorderSet<string, StringToIntDef> map;
map.Insert("1111", "1111");
map.Insert("2222", "2222");
map.Insert("3333", "3333");
map.Insert("4444", "4444");
map.Insert("1111", "4444");
cout << map["4444"] << endl;
cout << map["5555"] << endl;
UnorderSet<string, StringToIntDef>::Iterator it = map.Begin();
while (it != map.End())
{
cout << (*it).first << "--->" << (*it).second << endl;
++it;
}
cout << endl;
cout << "unorderedMAp中桶的个数:" << map.BucketCount() << endl;
cout << "5号桶中元素的个数:" << map.BucketSize(5) << endl;
}
测试函数·:
Test.cpp
//#include"hashtable.h"
//#include"HashBucket.h"
//#include"UnorderMap.h"
#include"UnorderSet.h"
int main()
{
UnorderSetTest();
//UnorderMapTest();
//HashBucketTest();
//HashTableTest();
return 0;
}