哈希表(开散列)的实现

目录

引入

开散列的底层实现

哈希表的定义

哈希表的扩容

哈希表的插入

哈希表查找

哈希表的删除


引入

接上一篇,我们使用了闭散列的方法解决了哈希冲突,此篇文章将会使用开散列的方式解决哈希冲突,后面对unordered_set和unordered_map的封装也会用开散列的哈希表实现。

开散列,也叫哈希桶和拉链法,其数组中存储的不再是单个数据,而是一个个的节点指针。通过将数据映射到对应位置后,插入到链表中去。

闭散列就像一个个的链表挂在数组上。


开散列的底层实现

与闭散列不同的是,开散列哈希表中存储的是节点,不再是具体数据;也不许需要再存储状态,只需要存储下一个指针即可。

哈希表的定义

cpp 复制代码
template<class K,class V>
struct HashNode
{
	//默认构造
	HashNode(const pair<K,V>& kv=pair())
		:_kv(kv)
		,next(nullptr)
	{ }

	pair<K, V> _kv;
	HashNode* _next;
};

template<class K,class V>
class HashTable
{
	typedef HashNode<K, V> Node;
public:
	//默认构造
	HashTable()
	{
		_table.resize(10);  //初始情况下数组有10个空间
		_n = 0;
	}

private:
	vector<Node*> _table;
	size_t _n;   //存储有效数据
};

哈希表的扩容

开散列在插入时,像闭散列一样也要检查载荷因子时候满足条件。开散列的载荷因子没有闭散列那么严格,开散列的载荷因子要求小于1即可,平均每条链有一个数据。

在扩容后也需要对数据进行重新插入。

扩容方法:创建新的哈希表,将原数据的节点一个个的插入到新数组中,再将两个数组进行交换。

cpp 复制代码
//扩容
void More()
{
	if ((double)_n / _table.size() >= 1)
	{
		//进行扩容
		HashTable newtable;
		size_t newsize = 2 * _table.size();
		newtable._table.resize(newsize);  //扩容两倍扩

		size_t hashi = 0;
		while (hashi < _table.size())
		{
			if (_table[hashi])
			{
				//将数据进行头插
				Node* pcur = _table[hashi];
				while (pcur)
				{
					Node* next = pcur->_next;
					pcur->_next = newtable._table[hashi];
					newtable._table[hashi] = pcur;

					pcur = next;
				}
			}
			hashi++;
		}
		_table.swap(newtable._table);
	}
}

哈希表的插入

cpp 复制代码
//插入
bool insert(const pair<K, V>& kv)
{
	//扩容
	More();

	size_t hashi = kv.first % _table.size();
	
	Node* newnode = new Node(kv);
	//将数据头插到对应映射的位置
	newnode->_next = _table[hashi];
	_table[hashi] = newnode;
	_n++;
	return true;
}

哈希表查找

先找到映射的位置,在对应位置的链表中查找。

cpp 复制代码
//查找
bool Find(const K& key)
{
	size_t hashi = key % _table.size();
	Node* pcur = _table[hashi];

	while (pcur)
	{
		if (pcur->_kv.first == key)
		{
			return true;
		}
		pcur = pcur->_next;
	}
	return false;
}

哈希表的删除

哈希表的删除比较简单:直接将该节点的前后指针连起来即可。

cpp 复制代码
//删除
bool Erase(const K& key)
{
	size_t hashi = key % _table.size();
	Node* pcur = _table[hashi];
	Node* parent = nullptr;
	while (pcur)
	{
		if (pcur->_kv.first == key)
		{
			if (parent == nullptr)
			{
				delete pcur;
				pcur = nullptr;
				_table[hashi] = nullptr;
			}
			else
			{
				parent->_next = pcur->_next;
				delete pcur;
			}
			return true;
		}
		pcur = pcur->_next;
	}
	return false;
}

到此,哈希表的开散列的基本实现已经完成。还有一些具体细节将在《哈希表的封装》中进行具体分析。

相关推荐
仰泳的熊猫4 小时前
题目2570:蓝桥杯2020年第十一届省赛真题-成绩分析
数据结构·c++·算法·蓝桥杯
似水明俊德8 小时前
02-C#.Net-反射-面试题
开发语言·面试·职场和发展·c#·.net
Thera7778 小时前
C++ 高性能时间轮定时器:从单例设计到 Linux timerfd 深度优化
linux·开发语言·c++
罗超驿8 小时前
独立实现双向链表_LinkedList
java·数据结构·链表·linkedlist
无限大69 小时前
AI实战03:Java开发岗专属工作流|用AI辅助代码审查与文档生成
面试
君义_noip10 小时前
信息学奥赛一本通 1952:【10NOIP普及组】三国游戏 | 洛谷 P1199 [NOIP 2010 普及组] 三国游戏
c++·信息学奥赛·csp-s
程序员雨果10 小时前
软件测试工程师:面试题与经验分享
软件测试·面试·职场和发展
Yvonne爱编码10 小时前
2026年计算机专业求职指南:从简历优化到技术面试通关【科普类】
面试·职场和发展
测试界的飘柔10 小时前
月薪 20k 的性能测试面试题大曝光,让你如何迅速拿下 offer!
自动化测试·软件测试·功能测试·面试·职场和发展·职场经验·找工作
努力也学不会java10 小时前
【缓存算法】一篇文章带你彻底搞懂面试高频题LRU/LFU
java·数据结构·人工智能·算法·缓存·面试