上节我们介绍了闭散列法实现哈希表的操作,今天我们来看开散列法实现哈希桶的操作。
开散列法(链地址法(开链法))
开散列法首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点组成 一个向量,因此,向量的元素个数与可能的桶数一致。
设元素的关键码为37,25,14,36,49,68,57,11,散列表为HT[12],表的大小为12,散列函数为Hash(x) = x % 11 Hash(37)=4 Hash(25)=3 Hash(14)=3 Hash(36)=3
Hash(49)=5 Hash(68)=2 Hash(57)=2 Hash(11)=0
通常,每个桶中的同义词子表都很短,设有n个关键码通过某一个散列函数,存放到散列表中的m个桶 中,那么每一个桶中的同义词子表的平均长度为n/m。这样以搜索平均长度为n/m的同义词子表代替了 搜索长度为n的顺序表,搜索效率快的多。
应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上,由于开地址法必须保持 大量的空闲空间以确保搜索效率,如二次探查法要求装载因子a <= 0.7 而表项所占空间又比指针大 的多,所以使用链地址法反而比开地址法节省存储空间。
在此次操作中,我们对关键元素key 和 下标value 值用键值对(pair<K,V>)进行封装,并对程序加入了迭代器,增加了难度。
但是原理不变。
(1)仿函数
class KeyToIntDef
{
public:
size_t operator()(const size_t& key)
{
return key;
}
};
class stringToInt
{
public:
size_t operator()(const string& key)
{
return BKDRHash(key.c_str());
}
private:
static size_t BKDRHash(const char* str)
{
unsigned int seed = 131;
unsigned int hash = 0;
while (*str)
{
hash = hash*seed + (*str++);
}
return (hash & 0x7FFFFFFF);
}
};
(2)素数表增容
const int _PriSize = 28;
static size_t GetNextPrime(const size_t& value)
{
static const unsigned long _PrimeList[_PriSize] =
{
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 (int i = 0; i < _PriSize; i++)
{
if (_PrimeList[i]>value)
return _PrimeList[i];
}
return _PrimeList[_PriSize - 1];
}
(3)哈希桶的结点元素
template<class K, class V>
struct HashBucketNode
{
HashBucketNode(const pair<K, V>& key)
:_key(key)
, _PNext(NULL)
{}
pair<K, V> _key;
HashBucketNode<K, V>* _PNext;
};
(4)迭代器对指针的封装
包括构造函数,拷贝构造函数,前置++,后置++,->的重载,*的重载,判断是否相等或不相等等操作;
//迭代器
template<class K,class V,class KToInt>
class HashBucketIterator
{
typedef HashBucketNode<K, V> Node;
typedef Node* PNode;
typedef HashBucketIterator<K, V, KToInt> IteratorSelf;
public:
PNode _Pcur; //迭代器成员变量
HashBucket<K, V, KToInt>* _hb; //指向哈希表的指针
public:
HashBucketIterator()
:_Pcur(NULL)
,_hb(NULL)
{}
HashBucketIterator(const PNode pcur, HashBucket<K, V, KToInt>*hb)
:_Pcur(pcur)
, _hb(hb)
{}
HashBucketIterator(const IteratorSelf& s)
:_Pcur(s._Pcur)
, _hb(s._hb)
{}
IteratorSelf& operator++()
{
Next();
return *this;
}
IteratorSelf operator++(int)
{
IteratorSelf temp(*this);
Next();
return temp;
}
pair<K, V>& operator*()
{
return _Pcur->_key;
}
pair<K, V>* operator->()
{
return &(operator*());
}
bool operator==(const IteratorSelf& s)
{
return _Pcur == s._Pcur;
}
bool operator != (const IteratorSelf& s)
{
return !(operator==(s));
}
private:
void Next()
{
if (_Pcur->_PNext)
_Pcur = _Pcur->_PNext;
else
{
//找下一个哈希桶
size_t bucketNo = _hb->HashFunc(_Pcur->_key.first) + 1;
for (; bucketNo < _hb->BucketCount(); bucketNo++)
{
if (_hb->_table[bucketNo])
{
_Pcur = _hb->_table[bucketNo];
return;
}
}
_Pcur = NULL;
}
return;
}
};
(5)哈希桶的实现
template<class K,class V,class KToInt>
class HashBucket
{
typedef HashBucketNode<K, V> Node;
typedef Node* PNode;
friend HashBucketIterator<K, V, KToInt>;
public:
typedef HashBucketIterator<K, V, KToInt> Iterator;
public:
HashBucket( size_t capacity=10)
:_size(0)
{
capacity = GetNextPrime(capacity);
_table.resize(capacity);
}
Iterator Begin()
{
for (size_t bucketNo = 0; bucketNo < _table.capacity(); ++bucketNo)
{
if (_table[bucketNo])
return Iterator(_table[bucketNo], this);
}
return Iterator(NULL, this);
}
Iterator End()
{
return Iterator(NULL, this);
}
//插入重复元素
pair<Iterator, bool> InsertEqual(const pair<K, V>& key)
{
CheckCapacity();
size_t addre = HashFunc(key.first);
PNode pcur = _table[addre];
PNode newNode = new Node(key);
//头插
newNode->_PNext = pcur;
_table[addre] = newNode;
_size++;
return make_pair(Iterator(newNode, this), true);
}
//插入唯一元素
pair<Iterator, bool> InsertUnique(const pair<K, V>& key)
{
CheckCapacity();
size_t addre = HashFunc(key.first);
PNode pcur = _table[addre];
//查找是否有相同元素
while (pcur)
{
if (pcur->_key.first == key.first)
return make_pair(Iterator(NULL,this), false);
pcur = pcur->_PNext;
}
//插入结点
PNode newNode = new Node(key);
newNode->_PNext = _table[addre];
_table[addre] = newNode;
_size++;
return make_pair(Iterator(newNode, this), true);
}
//查找元素
Iterator Find(const pair<K,V>& key)
{
size_t addre = HashFunc(key.first);
PNode pcur = _table[addre];
while (pcur)
{
if (pcur->_key.first == key.first)
return Iterator(pcur, this);
pcur = pcur->_PNext;
}
return Iterator(NULL, this);
}
//删除唯一元素
pair<Iterator, bool> EraseUnique(const pair<K, V>& key)
{
size_t addre = HashFunc(key.first);
PNode pcur = _table[addre];
PNode pre = NULL;
if (_table[addre] == NULL)
return make_pair(Iterator(NULL, this), false);
while (pcur)
{
if (pcur->_PNext->_key.first == key.first)
{
if (pcur == _table[addre])
{
_table[addre] = NULL;
}
else
{
pre->_PNext = pcur->_PNext;
}
delete pcur;
pcur = NULL;
--size;
return make_pair(Iterator(pre, this), true);
}
pre = pcur;
pcur = pcur->_PNext;
}
return make_pair(Iterator(NULL, this), false);
}
//删除值不唯一
pair<Iterator, bool> EraseEqual(const pair<K, V>& key)
{
size_t addre = HashFunc(key.first);
PNode pcur = _table[addre];
PNode pre = NULL;
if (_table[addre] == NULL)
return make_pair(Iterator(NULL, this), false);
while (pcur)
{
if (pcur->_key.first == key.first)
{
if (pcur == _table[addre])
{
_table[addre] = pcur->_PNext;
delete pcur;
pcur = NULL;
}
else
{
pre->_PNext = pcur->_PNext;
delete pcur;
pcur = pcur->_PNext;
}
_size--;
}
else
{
pre = pcur;
pcur = pre->_PNext;
}
}
return make_pair(Iterator(NULL, this), false);
}
//值为key 的元素个数
size_t Count(const pair<K, V>& key)
{
size_t addre = HashFunc(key.first);
size_t count = 0;
PNode pcur = _table[addre];
while (pcur)
{
if (pcur->_key.first == key.first)
count++;
pcur = pcur->_PNext;
}
return count;
}
//桶的个数
size_t BucketCount()const
{
return _table.capacity();
}
//桶内元素的个数
size_t BucketElemSize(size_t bucketNo)const
{
size_t count = 0;
PNode pcur = _table[bucketNo];
while (pcur)
{
count++;
pcur = pcur->_PNext;
}
return count;
}
//查找值为key,如果找到了,返回对应的value;如果没有找到,则插入该节点,返回缺省的value。
V& FindOrInsert(const pair<K,V>& key)
{
size_t addre = HashFunc(key.first);
PNode pcur = _table[addre];
while (pcur)
{
if (pcur->_key.first == key.first)
return pcur->_key.second;
pcur = pcur->_PNext;
}
pair<Iterator, bool> ret = InsertUnique(make_pair(key.first, V()));
return (*(ret.first)).second;
}
//判断是否为空
bool Empty()const
{
return _size == 0;
}
//插入的总数
size_t numsize()const
{
return _size;
}
//清空
void clear()
{
for (size_t bucketNo = 0; bucketNo < _table.capacity(); ++bucketNo)
{
PNode pcur = _table[bucketNo];
while (pcur)
{
PNode pDel = pcur->_PNext;
delete pcur;
pcur = pDel;
_size--;
}
}
}
~HashBucket()
{
clear();
}
private:
void CheckCapacity()
{
size_t capacity = _table.capacity();
if (_size==capacity)
{
size_t newcapacity = GetNextPrime(capacity);
HashBucket<K, V, KToInt> hb(newcapacity);
for (size_t bucketNo = 0; bucketNo < capacity; bucketNo++)
{
PNode pcur = _table[bucketNo];
while (pcur)
{
hb.InsertEqual(pcur->_key);
pcur = pcur->_PNext;
}
}
swap(hb._table, _table);
_size = hb._size;
}
}
size_t HashFunc(const K& key)
{
return KToInt()(key) % _table.capacity();
}
private:
vector<PNode> _table;
size_t _size;
};
(6)测试函数
void test()
{
HashBucket<int, int, KeyToIntDef> hb(20);
hb.InsertUnique(make_pair(2,2));
hb.InsertUnique(make_pair(3, 3));
hb.InsertUnique(make_pair(4, 4));
hb.InsertUnique(make_pair(2, 5));
/*hb.InsertEqual(make_pair(3, 3));
hb.InsertEqual(make_pair(3, 4));
hb.EraseEqual(make_pair(3, 3));*/
/*cout<<hb.FindOrInsert(make_pair(2, 2));
cout << hb.FindOrInsert(make_pair(9, 2));*/
HashBucket<int, int, KeyToIntDef>::Iterator it = hb.Begin();
if (!hb.Empty())
{
while (it != hb.End())
{
cout << it->first << " ";
cout << (*it).second << endl;
it++;
}
cout << hb.BucketCount();
cout << hb.numsize();
}
//HashBucket<string, string, stringToInt>hb2(20);
//hb2.InsertUnique(make_pair("1111", "1111"));
//hb2.InsertUnique(make_pair("2222", "2222"));
//hb2.InsertUnique(make_pair("3333", "3333"));
//hb2.InsertUnique(make_pair("2222", "1111"));
//cout << hb2.BucketCount();
//cout << hb2.numsize();
cout << hb2.FindOrInsert(make_pair("2222", "2222"));
cout << hb2.FindOrInsert(make_pair("4444", "4444"));
}
大家可以自己利用哈希桶封装unordered_map啊!