哈希表的结构有闭散列和开散列,闭散列的问题就在于, 产生哈希冲突太多的时候,就会是哈希表的性能下降. 而开散列的优点在在于. 它在哈希表的每一行键值位置下面挂了一个链表, 哈希表中存放链表头结点的指针, 所以这里的 vector 是一个指针数组, 当产生哈希冲突的时候, 只需要在相同键值的链表下面在挂一个节点就可.这样的到的就像一个个桶一样, 因此也叫哈希桶
哈希桶与结点
template<class K,class V>
struct HashNode{
HashNode(const pair<K,V>& data=pair<K,V>())
:_data(data)
,_next(nullptr)
{}
pair<K,V> _data;
HashNode<K,V>* _next;
};
template<class K,class V>
class HashTable{
public:
typedef HashNode<K,V> Node;
typedef Node* pNode;
HashTable(const int n=10){
_ht.resize(10);
_size=0;
}
private:
vector<pNode> _ht;
size_t _size;
};
插入
哈希桶的插入分为
- 检测是否需要增容
- 计算索引的位置
- 遍历链表, 查看插入的元素是否已经在桶中
- 在桶中则插入失败,否则创建节点,插入
bool Insert(const pair<K,V>& data){
HasaCapacity();
//计算索引
int index=data.first % _ht.size();
pNode cur=_ht[index];
//判断元素是否在表中
while(cur){
if(cur->_data.fiest==data.fiest)
return false;
cur=cur->_next;
}
//创建节点
cur=new Node(data);
//头插
cur->_next=_ht[index];
_ht[index]=cur;
return true;
}
增容
void HashCapacity(){
if(_szie==_ht.szie()){
int newC=_size==0?10:2*_ht.size();
HashTable<K,V> newT;
newT.resize(newC);
for(size_t i=0;i<_ht.size();i++){
pNode cur=_ht[i]
while(cur){
pNode next=cur->_next;
int index=cur->_data.first%newT.size();
cur->next=newT[index];
newT[index]=cur;
cur->_next=next;
}
_ht[i]=nullptr;
}
swap(_ht,newT);
}
}
删除
bool Erase(const K& key){
int index = key%_ht.size();
pNode cur = _ht[index];
pNode prev = nullptr;
while (cur){
if (cur->_data.first == key){
if (prev == nullptr)
_ht[index] = nullptr;
else
prev->_next = cur->_next;
delete cur;
cur = nullptr;
return true;
}
prev = cur;
cur = cur->_next;
}
return false;
}
查找
pNode Find(const K& key){
//计算索引,找到在表桶的位置
int index = key % _ht.size();
pNode cur = _ht[index];
while (cur){
if (cur->_data.first == key)
return cur;
cur = cur->_next;
}
return nullptr;
}
哈希表的迭代器
由于哈希表的存储是按照哈希函数来计算对应的键值来存储的, 因此要访问表中的元素,需要借助迭代器来实现.
对于开散列的哈希表, 他的每一个迭代器就是链表中的结点, 因此对迭代器进行封装是 -> * ++ != begin() end() 因为在封装 ++ 不仅要对桶中的结点进行操作,有时还有对表进行操作,所以迭代器中还需要一个表的指针 下面 重点对++操作进行分析,有以下集中情况:
- 当前迭代器的下一个还有结点时,直接走到下一个
- 当前迭代器位于链表的末尾,鄙视需要,找到下一个不为空的哈希桶.
- 当前迭代器位于最后一个不为空的哈希桶的末尾,则下一次为空
template<class K,class V>
class _HashIterator{
public:
typedef HashNode<K,V> Node;
typedef Node* pNode;
typedef HashTable<K,V> HTable;
typedef _HashIteraror<K,V> Self
_HashIterator(pNode node,HTable* ptr)
:_node(node)
,_ptr(ptr)
{}
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{
//计算出当前哈希桶的位置
int index=_node->_data.first%_ptr->_ht.size();
++index;
//寻找下一个不为空的哈希桶
while(index<_ptr->_ht.size()){
if(_ptr->_ht[index]!=nullptr){
_node=_ptr->_ht[index];
break;
}
++index;
}
//哈希表中的最后一个节点
if(index==_ptr->_ht.size())
_node=nullptr;
}
return *this;
}
private:
pNode _node;
HTable* _ptr;
};
哈希表中使用的迭代器
template<class K,class V>
class HashTable{
public:
********
typedef _HashTterator<K,V> iterator;
//返回值中的哈希表的指针正式当前对象(表),因此传入this指针
iterator begin(){
for(size_t i=0;i<_ht.size();i++){
if(_ht[i])
return iterator(_ht[i],this);
}
return iterator(nullptr,this);
}
iterator end(){
return iterator(nullptr,this);
}
********
};