前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C++进阶篇】哈希表的模拟实现(赋源码)

【C++进阶篇】哈希表的模拟实现(赋源码)

作者头像
熬夜学编程的小王
发布于 2025-05-30 01:31:46
发布于 2025-05-30 01:31:46
10600
代码可运行
举报
文章被收录于专栏:编程小王编程小王
运行总次数:0
代码可运行

前言

哈希表的核心思想就是映射,通过将key值以某种算法映射成不大于哈希表长度的哈希值,从而实现存储数据。上篇提到解决哈希冲突有 闭散列 和 开散列,本文将用这两种理论思想实现哈希表。 代码位置:哈希模拟的实现源码

一. 开放地址法实现哈希表

1.1 闭散列结构定义

该闭散列哈希表使用vector数组,其中数组里面包含一个结构体,该结构存储pair类型数据及当前位置的状态,是否为空,不为空,或者有数据然后被删除了的三个状态,伪代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//节点可能的三种状态
enum State
{
	EXIST,
	EMPTY,
	DELETE
};

//哈希表中存储的数据
template<class K, class V>
struct HashData
{
	pair<K, V> _kv;//存放数据类型
	State _state = EMPTY;//当前位置的状态,默认为空,即表示此位置没有数据
};
template<class K, class V, class Hash = HashFunc<K>>
class HashTable
{
public:
private:
	vector<HashData<K, V>> _tables;
	size_t _n = 0;//表中实际存储的数据个数
};
  • 仿函数作用:

因为里面是数组,通过key值映射成哈希值后,哈希值不能超过哈希表的长度,需要取模,所以同时也要传入仿函数,将任意类型数据不能取模转换成整数,本来就可以进行取模运算的,就无需借助额外的仿函数,所以就可以给一个默认的仿函数,该仿函数给本来是整数去使用的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//默认的仿函数
template<class K>
class HashFunc
{
public:
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

1.2 构造函数

给vector数组开一定的空间,为了防止2的幂次方的数,底层给了一定合理的素数,下面给代码即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
inline unsigned long __stl_next_prime(unsigned long n)
{
	// Note: assumes long is at least 32 bits.
	static const int __stl_num_primes = 28;
	static const unsigned long __stl_prime_list[__stl_num_primes] =
	{
		53, 97, 193, 389, 769,
		1543, 3079, 6151, 12289, 24593,
		49157, 98317, 196613, 393241, 786433,
		1572869, 3145739, 6291469, 12582917, 25165843,
		50331653, 100663319, 201326611, 402653189, 805306457,
		1610612741, 3221225473, 4294967291
	};
	const unsigned long* first = __stl_prime_list;
	const unsigned long* last = __stl_prime_list + __stl_num_primes;
	const unsigned long* pos = lower_bound(first, last, n);
	return pos == last ? *(last - 1) : *pos;
}

HashTable()
{
	_tables.resize(__stl_next_prime(1));
}

1.3 插入(线性探测)

插入之前判断该key值是否在存在,存在直接返回即可。对插入的key值映射成哈希值,然后进行探测,探测规则:当前位置为空,继续向后探测,跳出循环后,将该位置插入数据并将该位置的状态设置为存在,_n++(表示存储的数据个数+1),这里有一个问题:扩容???

  • 问题:如何扩容
1.3.1 传统写法

创建一个数组(数组已经扩容后),将旧表中的vector数组中的数据重新进行映射成新开的数组(这里的一个优势在于:原来在旧表中冲突的数据可能在新表中就不冲突了),也需进行探测,过程基本与插入过程一致,将旧数据完成探测之后,在新表与旧表进行交换。伪代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool Insert(const pair<K, V>& kv)
{
	if (Find(kv.first))
		return false;

	//扩容
	if ((double)_n / (double)_tables.size() >= 0.7)
	{
		// 获取素数表里面比当前表大的下一个素数
		size_t newSize = __stl_next_prime(_tables.size() + 1);
		vector<HashData<K, V>> newTables(newSize);
		//遍历旧表,将数据都映射到新表
		for (size_t i = 0; i < _tables.size(); i++)
		{
			if (_tables[i]._state == EXIST)
			{
				size_t hash0 = _tables[i].kv.first % newSize;
				size_t i = 1;
				size_t hashi = hash0;
				while (newTables[hashi]._state == EXIST)
				{
					//冲突探测
					hashi = (hash0 + i) % newSize;
					i++;
				}

				newTables[hashi]._kv = kv;
				newTables[hashi]._state = EXIST;
				//_n++;需不需要写???思考一下
			}
		}
			_tables.swap(newTables);

		
	}

	Hash hs;
	size_t hash0 = hs(kv.first) % _tables.size();
	size_t i = 1;
	size_t hashi = hash0;
	while (_tables[hashi]._state == EXIST)
	{
		//冲突探测
		hashi = (hash0 + i) % _tables.size();
		i++;
	}

	_tables[hashi]._kv = kv;
	_tables[hashi]._state = EXIST;
	_n++;

	return true;
}
  • 细节1:

重新寻找哈希值,需要对新的哈希表的长度进行取余,不要依然对旧表中的哈希表长度进行取余,否则会导致新表中新增加的长度没有被使用,冲突概率未减少。

  • 细节2:

_n++需不需要写???不需要,因为是将旧表中的数据重新映射至新表,数据并没有增加。

注意:可以看出上述代码有点冗余,特别是插入过程与重新建立映射关系的代码很相似。 下面这种写法很巧妙,且代码量少。

1.3.2 现代写法

思想:建立一个新表,然后给新表中的哈希表开一定足够的空间,然后将旧表中的数据直接插入新表中即可,插入的过程同时也完成探测过程。很优雅的写法,建议采用它。

  • 伪代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool Insert(const pair<K, V>& kv)
{
	if (Find(kv.first))
		return false;

	//扩容
	if ((double)_n / (double)_tables.size() >= 0.7)
	{
		size_t newSize = __stl_next_prime(_tables.size() + 1);
		HashTable<K, V, Hash> newHT;
		newHT._tables.resize(newSize);

		//遍历旧表,将数据都映射到新表
		for (size_t i = 0; i < _tables.size(); i++)
		{
			if (_tables[i]._state == EXIST)
			{
				newHT.Insert(_tables[i]._kv);//此行为已经将旧数据映射到了新表中,同时进行线性探测和冲突探测,可能原来在旧表中的数据冲突,在新表中它就不冲突了
			}
		}

		_tables.swap(newHT._tables);//将新表与旧表进行交换
	}

	Hash hs;
	size_t hash0 = hs(kv.first) % _tables.size();
	size_t i = 1;
	size_t hashi = hash0;
	while (_tables[hashi]._state == EXIST)
	{
		//冲突探测
		hashi = (hash0 + i) % _tables.size();
		i++;
	}

	_tables[hashi]._kv = kv;
	_tables[hashi]._state = EXIST;
	_n++;

	return true;
}

该实现是一个功能完整的线性探测哈希表,适合学习用途。

1.4 查找

查找过程很简单,首先对要查找的key值转化成映射后的哈希值,对应位置状态为!EXIST,进行查询当前位置状态不为空,且不为删除直接返回该指针即可,否则继续往后探测,当探测为空,说明不存在,直接返回nullptr即可。

  • 伪代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
HashData<K, V>* Find(const K& key)
{
	Hash hs;
	size_t hash0 = hs(key) % _tables.size();
	size_t hashi = hash0;
	size_t i = 1;
	while (_tables[hashi]._state != EMPTY)
	{
		if (_tables[hashi]._state == EXIST
			&& _tables[hashi]._kv.first == key)
		{
			return &_tables[hashi];
		}

		hashi = (hash0 + i) % _tables.size();
		++i;
	}

	return nullptr;
}

1.5 删除

直接调用接口Find即可,接收返回值,判断返回值是否为空,为空直接返回false;不为空将该位置的状态设置为删除即可,然后返回true即可。

  • 伪代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool Erase(const K& key)
{
	HashData<K, V>* ret = Find(key);
	if (ret == nullptr)
	{
		return false;
	}
	else
	{
		--_n;
		ret->_state = DELETE;
		return true;
	}
}

标记删除操作时间复杂度为 O(1),无数据迁移开销。 声明:上述做法了解一下即可,下面这个才是王炸,需重点关注及掌握。

二. 链地址法实现哈希表(哈希桶)

2.1 开散列结构定义

该开散列哈希表使用vector数组,其中数组里面包含一个哈希表节点,该节点存储pair类型数据及下一个哈希表数据的指针。伪代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template<class K, class V>
struct HashNode {
    pair<K, V> _kv;          // 键值对
    HashNode<K, V>* _next;   // 冲突链指针
    HashNode(const pair<K, V>& kv) : _kv(kv), _next(nullptr) {} // 构造函数
};

template<class K, class V>
class HashTable {
    typedef HashNode<K, V> Node;
    vector<Node*> _tables;    // 哈希槽数组(存储链表头指针)
    size_t _n = 0;            // 当前元素总数
};

该代码提供了链地址法哈希表的核心骨架。

2.2 构造函数

构造函数与上述闭散列一致。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
inline unsigned long __stl_next_prime(unsigned long n)
{
	// Note: assumes long is at least 32 bits.
	static const int __stl_num_primes = 28;
	static const unsigned long __stl_prime_list[__stl_num_primes] =
	{
		53, 97, 193, 389, 769,
		1543, 3079, 6151, 12289, 24593,
		49157, 98317, 196613, 393241, 786433,
		1572869, 3145739, 6291469, 12582917, 25165843,
		50331653, 100663319, 201326611, 402653189, 805306457,
		1610612741, 3221225473, 4294967291
	};
	const unsigned long* first = __stl_prime_list;
	const unsigned long* last = __stl_prime_list + __stl_num_primes;
	const unsigned long* pos = lower_bound(first, last, n);
	return pos == last ? *(last - 1) : *pos;
}

HashTable()
{
	_tables.resize(__stl_next_prime(1), nullptr);
}

2.3 插入

创建一个数组,扩容时,将旧表中的节点挪动至新表中,需重新建立映射关系,也需要进行探测,过程与插入过程一致,将旧数据完成探测之后,在新表与旧表进行交换。伪代码如下: **插入数据时有个问题,**进行尾插还是头插,两者都可以,但头插很简单,尾插相对较复杂,需要找尾,当前桶挪动完毕,需要将当前桶只为nullptr,本文以头插为示例。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool Insert(const pair<K, V>& kv)
{
	if (Find(kv.first))
		return false;

	if (_n == _tables.size())
	{
		size_t newSize = __stl_next_prime(_tables.size() + 1);
		vector<Node*> newtables(newSize, nullptr);
		//遍历旧表,将旧表的节点挪动到新表
		for (size_t i = 0; i < _tables.size(); i++)
		{
			Node* cur = _tables[i];
			while (cur)
			{
				Node* next = cur->_next;

				size_t hashi = cur->_kv.first % newSize;
				cur->_next = newtables[hashi];
				newtables[hashi] = cur;
			}

			_tables[i] = nullptr;
		}

		_tables.swap(newtables);
	}

	size_t hashi = kv.first % _tables.size();
	Node* newnode = new Node(kv);

	//头插,尾插也行,找尾麻烦,需要找尾
	newnode->_next = _tables[hashi];
	_tables[hashi] = newnode;
	++_n;
	return true;
}

2.4 查找

查找过程很简单,将要查找的key值转换成哈希值,然后对该桶进行遍历,判断桶中的数据key值是否与要查找的key值是否相等,相等返回该节点指针即可,找到空还未找到,返回nullptr即可。

  • 伪代码如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Node* Find(const K& key)
{
	size_t hashi = key % _tables.size();
	Node* cur = _tables[hashi];

	while (cur)
	{
		if (cur->_kv.first == key)
		{
			return cur;
		}

		cur = cur->_next;
	}

	return nullptr;
}

2.5 删除

删除需要前驱指针(默认为空)将删除后的节点连接起来,删除过程相对来说较复杂,也需要要删除的key值转换成哈希值,然后遍历该桶,判断桶中节点的数据key值与要查找的key值是否相等,相等进行删除,返回true即可,里面有细节 -> :

  • 细节一:

当删除头结点时,头结点没有前驱指针,会对空指针进1行解引用,所以需要分情况讨论。

  1. 前驱指针不为空,和为空两种状态。
  2. 不相等继续往后找,当前桶找完还未找到,返回false即可。
  • 伪代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
		bool Erase(const K& key)
		{
			size_t hashi = key % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					//删除
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}

					delete cur;
					_n--;
					return true;
				}

				//不相等继续往后找
				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

代码正确处理了链表节点的删除,维护了计数器_n,但未处理重复键(若允许存在)。

三. 最后

本文详述了哈希表的两种实现方式:开放地址法(闭散列)与链地址法(开散列)。开放地址法通过线性探测解决冲突,采用标记删除优化性能,扩容时重建哈希表;链地址法以链表处理冲突,头插法简化操作,扩容时重新映射所有节点。两者均使用素数表优化哈希分布,核心操作包括插入、查找、删除,适用于不同场景,是高效键值存储的关键技术。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
2 条评论
热度
最新
哈哈哈 我突然又搜到其实可以直接用sklearn里的from sklearn.datasets import load_iris iris_dataset = load_iris()
哈哈哈 我突然又搜到其实可以直接用sklearn里的from sklearn.datasets import load_iris iris_dataset = load_iris()
回复回复点赞举报
非常感谢 万分感谢 真的感谢 痛哭流涕
非常感谢 万分感谢 真的感谢 痛哭流涕
回复回复点赞举报
推荐阅读
Go项目实战--数据Dao层代码的单元测试实战
上节课我给大家介绍了怎么给Go项目做单元测试的规划,当然这里仅限于跟咱们课程里的实战项目一样分层架构设计做的还可以的项目哦,要是所有逻辑都耦合在Controller里,那这个规划就不适用了。。。,所有逻辑都耦合在Controller里还做个锤子的单元测试,直接上线让用户给你测(手机系统都能这么干的。。。你们怕啥)
KevinYan
2025/04/23
1030
Go项目实战--数据Dao层代码的单元测试实战
白话Golang单元测试
最近学习某个 Golang 单元测试的课程,发现其中推荐使用 gomonkey 这种黑科技,让人略感意外,毕竟在软件开发领域,诸如依赖注入之类的概念已经流传了几十年了,本文希望通过一个例子的演化过程,来总结出 Golang 单元测试的最佳实战。
LA0WAN9
2021/12/14
5180
从头到脚说单测——谈有效的单元测试
导语 非常幸运的是,从4月份至今,我能够全身心投入到腾讯新闻的单元测试专项任务中,从无知懵懂,到不断深入理解的过程,与开发同学互帮互助,受益匪浅。在此过程中,得到了质量总监、新闻总监和乔帮主的倾囊指导,真心感谢!!我希望把所有心得,总结成一篇较为全面的文章,分享给其他团队。时刻牢记:1. 不要滥用mock 2. 基于意图。 在我们谈到单元测试,大都清楚是测试函数符合预期,国外很多大公司都将单测执行的很好,国内成功的案例则相对有限。在本文中,笔者将在腾讯新闻项目中亲身经历单测从无到有的实践过程梳理为可读
腾讯技术工程官方号
2019/08/20
11.7K0
从头到脚说单测——谈有效的单元测试
深入解析GoConvey:Go测试工具的新选择
本文将为您详细介绍GoConvey这款在GitHub上广受欢迎的Go语言测试工具,尤其是它相对于Go标准库的testing库的优势,以及它们在定位上的不同。
运维开发王义杰
2023/08/10
7260
深入解析GoConvey:Go测试工具的新选择
一文了解一线互联网大厂的 Golang 单测最佳实战经验
Go 单测里面,最常见的就是通过 gomonkey(stub) 打桩或者 mocker(mock) 的模拟来替换掉我们原本的执行逻辑,因此首先我们要对这两种方式有一个比较深入的理解,要理解为何 Go 单测的时候能够替换掉原来的方法!!!
Allen.Wu
2023/03/01
2.7K0
一文了解一线互联网大厂的 Golang 单测最佳实战经验
Golang 单元测试详尽指引
文末有彩蛋。 作者:yukkizhang,腾讯 CSIG 专项技术测试工程师 本篇文章站在测试的角度,旨在给行业平台乃至其他团队的开发同学,进行一定程度的单元测试指引,让其能够快速的明确单元测试的方式方法。 本文主要从单元测试出发,对Golang的单元测试框架、Stub/Mock框架进行简单的介绍和选型推荐,列举出几种针对于Mock场景的最佳实践,并以具体代码示例进行说明。 一、单元测试 1. 单元测试是什么 单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向
腾讯技术工程官方号
2020/10/26
4.7K0
Go语言单元测试
学习Golang的时候遇到了一些单元测试问题,发现有些工具是真的好用,就记录在此,主要包括monkey、convey,还有数据库Mock等。
有财君
2023/03/21
7230
Go语言单元测试
Go:测试库(GoConvey,testify,GoStub,GoMonkey)对比及简介
assert库是这样的一个库,它有一系列函数来适应各种各样不同的场景需求,下面是一个简单的判断值是否符合预期的demo:
Freedom123
2024/03/29
6450
Go:测试库(GoConvey,testify,GoStub,GoMonkey)对比及简介
Golang 单元测试合集整理,(我最常用 gomonkey)欢迎收藏
无论写什么样的语言,单元测试都是必不可少的,它可以极大的提高我们的代码质量,减少各种低级错误和 bug
阿兵云原生
2023/09/14
2.1K0
Golang 单元测试合集整理,(我最常用 gomonkey)欢迎收藏
每天坚持20分钟编写测试
go tool cover -html=cover.out -o coverage_xxxxx.html
李子健
2022/07/06
1290
手把手教你如何进行 Golang 单元测试
点击上方蓝字,发现更多精彩 导语 本篇是对单元测试的一个总结,通过完整的单元测试手把手教学,能够让刚接触单元测试的开发者从整体上了解一个单元测试编写的全过程。最终通过两个问题,也能让写过单元测试的开发者收获单测执行时的一些底层细节知识。 引入 随着工程化开发在司内大力的推广,单元测试越来越受到广大开发者的重视。在学习的过程中,发现网上针对 Golang 单元测试大多从理论角度出发介绍,缺乏完整的实例说明,晦涩难懂的 API 让初学接触者难以下手。 本篇不准备大而全的谈论单元测试、笼统的介绍 Golang
腾讯VTeam技术团队
2021/06/02
1.5K0
一文说尽Golang单元测试实战的那些事儿
导语 | 单元测试,通常是单独测试一个方法、类或函数,让开发者确信自己的代码在按预期运行,为确保代码可以测试且测试易于维护。腾讯后台开发工程师张力结合了公司级漏洞扫描系统洞犀在DevOps上探索的经验,以Golang为例,列举了编写单元测试需要的工具和方法,然后针对写单测遇到的各种依赖问题,详细介绍了通过Mock的方式解决各种常用依赖,方便读者在写go语言UT的时候,遇到依赖问题,能够快速找到解决方案。最后再和大家探讨一下关于单元测试上的一些思考。 一、前言 单元测试,通常是单独测试一个方法、类或函数
腾讯云开发者
2021/07/28
1.4K0
Golang单元测试
Go提供了test工具用于代码的单元测试,test工具会查找包下以_test.go结尾的文件,调用测试文件中以 Test或Benchmark开头的函数并给出运行结果
仙人技术
2021/08/31
8340
Golang单元测试
Go项目实战-代码里有API调用时单元测试怎么做?
上一节课我给大家展示了项目Dao层里的CURD操作我们该怎么做单元测试,尤其是Insert、Update这些需要对数据库进行更改的操作怎么用sqlMock的方式让我们既不用真正对数据库发起操作也能验证这些操作是否符合预期。
KevinYan
2025/05/07
1110
Go项目实战-代码里有API调用时单元测试怎么做?
golang测试框架goconvey的使用
前面我们介绍了golang测试框架里面的testify, 下面让了解一下另一个用的也比较多的断言框架goconvey。
Johns
2021/08/31
3.9K0
从头到脚说单测——谈有效的单元测试(上篇)
作 者 杨迪,腾讯PCG高级工程师 商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。 作者导语 从4月份至今,我能够全身心投入到腾讯新闻的单元测试专项任务中,从无知懵懂,到不断深入理解的过程,与开发同学互帮互助,受益匪浅。在此过程中,得到了质量总监等等优秀同事的倾囊指导,真心感谢!!我希望把所有心得,总结成一篇较为全面的文章,分享给其他团队。时刻牢记:1. 不要滥用mock 2. 基于意图。 一. 为单元测试“正名” 我曾经认为,单元测试面向的是一个函数。任何走出一个函数的测试,
WeTest质量开放平台团队
2019/08/15
2.6K0
从头到脚说单测——谈有效的单元测试(上篇)
走近微服务,第4部分:使用GoConvey进行测试和模拟
应该如何测试微服务?在为这个特定领域制定测试方案时,需要考虑哪些特别的挑战?在本博客系列的第4部分中,我们将一窥究竟。
用户2176511
2018/06/20
3.5K0
走近微服务,第4部分:使用GoConvey进行测试和模拟
Golang 测试教程
How to write test with golang 代码示例 TDD(Test-Driven development) 测试驱动开发 内置的 testing 库 、 表格驱动、样本测试、TestMain 第三方:goconvey Monkey 猴子补丁 数据库 mock travisCI 代码覆盖率 TDD 快速实现功能 再设计和重构 软件测试 在指定的条件下,操作程序,发现程序错误 单元测试 对软件的组成单元进行测试,最小单位:函数 包含三个步骤: 指定输入 指定预期 函数结果和指定的预期
谢伟
2019/03/11
1.7K0
Golang 测试教程
『Go 语言学习专栏』-- 第十一期
12.png golang-11.png 大家好,我叫谢伟,是一名程序员。 最近更新不是很频繁,主要是我手头有好些事需要解决,比如更换环境,比如出去见识人,以便更好的认识自己,知道自己的短板在哪。 很早之前我就意识到:每隔半年需要出去走走,哪怕不是真的更换工作,你也应该出去走走,去市场检验一下自己是否在对应的岗位有竞争力,你的市场价位是多少。 好,本节的主题是:单元测试。 测试其实分很多种,就我在企业中的认识,一般称为测试工程师,从事的应该是所谓的集成测试(或者说是 AC测试(Acceptance C
谢伟
2018/06/06
5540
使用 gomonkey Mock 函数及方法
在 Golang 语言中,写单元测试的时候,不可避免的会涉及到对其他函数及方法的 Mock,即在假设其他函数及方法响应预期结果的同时,校验被测函数的响应是否符合预期。
CG国斌
2022/06/05
2.6K0
推荐阅读
相关推荐
Go项目实战--数据Dao层代码的单元测试实战
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 前言
  • 一. 开放地址法实现哈希表
    • 1.1 闭散列结构定义
    • 1.2 构造函数
    • 1.3 插入(线性探测)
      • 1.3.1 传统写法
      • 1.3.2 现代写法
    • 1.4 查找
    • 1.5 删除
  • 二. 链地址法实现哈希表(哈希桶)
    • 2.1 开散列结构定义
    • 2.2 构造函数
    • 2.3 插入
    • 2.4 查找
    • 2.5 删除
  • 三. 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档