开散列概念
开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表连接起来,各链表的头结点存储在哈希表中。开散列中每个桶中放的都是发生哈希冲突的元素。
节点定义
template<class V>
struct HashNode
{
HashNode(const V& data = V())
:_data(data)
, _next(nullptr)
{}
V _data;
HashNode<V>* _next;
};
迭代器
//迭代器
template <class K, class V, class KeyOfValue, class HFun>
class HashIterator
{
typedef HashNode<V> Node;
typedef Node* pNode;
typedef HashIterator<K, V, KeyOfValue, HFun> Self;
typedef HashTable<K, V, KeyOfValue, HFun> HTable;
template <class K, class V, class KeyOfValue, class HFun>
friend class HashTable;
HashIterator(pNode node, HTable* p)
:_node(node)
, _phs(p)
{}
V& operator*()
{
return _node->_data;
}
V* operator->()
{
return &_node->_data;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
Self& operator++()
{
if (_node->_next)
_node = _node->_next;
else
{
KeyOfValue kov;
size_t id = kov(_node->_data) % _phs->_hs.size();
++id;
while (id < _phs->_hs.size())
{
if (_phs->_hs[id])
{
_node = _phs->_hs[id];
break;
}
++id;
}
if (id == _phs->_hs.size())
_node = nullptr;
}
return *this;
}
private:
pNode _node;
HTable* _phs;
};
哈希表
template <class K, class V, class KeyOfValue, class HFun>
class HashTable
{
public:
typedef HashNode<V>Node;
typedef Node* pNode;
typedef HashIterator<K, V, KeyOfValue, HFun>iterator;
HashTable(size_t N = 10)
{
_hs.resize(N);
_size = 0;
}
iterator begin()
{
for (size_t i = 0; i < _hs.size(); i++)
{
if (_hs[i])
return iterator(_hs[i], this);
}
}
iterator end()
{
return iterator(nullptr, this);
}
size_t HashId(const K& key, size_t sz)
{
HFun hf;
return hf(key) % sz;
}
pair<iterator, bool>Insert(const V& data);
size_t GetPrime(size_t sz);
void CheckCapacity();
pNode find(const K& key);
bool Erase(const K& key);
private:
vector<pNode> _hs;//指针数组
size_t _size;
};
开散列与闭散列比较
应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上: 由于开地址法必须保持大量的空闲空间以确保搜索效率,而表项所占空间又比指针大的多,所以使用链地址法反而比开地址法节省存储空间。