哈希表(开散列)

哈希表的结构有闭散列和开散列,闭散列的问题就在于, 产生哈希冲突太多的时候,就会是哈希表的性能下降. 而开散列的优点在在于. 它在哈希表的每一行键值位置下面挂了一个链表, 哈希表中存放链表头结点的指针, 所以这里的 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;
};

插入
哈希桶的插入分为

  1. 检测是否需要增容
  2. 计算索引的位置
  3. 遍历链表, 查看插入的元素是否已经在桶中
  4. 在桶中则插入失败,否则创建节点,插入
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() 因为在封装 ++ 不仅要对桶中的结点进行操作,有时还有对表进行操作,所以迭代器中还需要一个表的指针 下面 重点对++操作进行分析,有以下集中情况:

  1. 当前迭代器的下一个还有结点时,直接走到下一个
  2. 当前迭代器位于链表的末尾,鄙视需要,找到下一个不为空的哈希桶.
  3. 当前迭代器位于最后一个不为空的哈希桶的末尾,则下一次为空
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);
    }

    ********

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值