😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍哈希表 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰: 2024-07-24 14:48:55
本文未经允许,不得转发!!!
目录
- 🎄一、概述
- 🎄二、键值对(key-value pair)
- 🎄三、哈希函数
- 🎄四、哈希冲突(哈希碰撞)
- ✨4.1 开发地址法
- ✨4.2 链地址法
- 🎄五、哈希表的使用
- 🎄六、总结
🎄一、概述
在学习过的简单数据结构当中,
数组的特点:访问(寻址)速度较快的、但插入、删除操作较慢;
链表的特点:访问(寻址)速度较慢的、但插入、删除操作很快;
所以,有些大牛就想着能不能结合这数组、链表的优点,造出一个 访问(寻址)速度较快的、插入、删除操作也很快 的数据结构,后面就造出来一个 哈希表。
哈希表(Hash Table):也叫做散列表。是根据关键码值(Key Value)直接进行访问的数据结构。
哈希表存储的都是键值对(Key-Value)
,即一个关键字对应一个内容,给出关键字就可以在哈希表中找到对应内容。
哈希表的工作过程:哈希表通过键值对的 关键字(key) 和 哈希函数 计算出对应的一个位置,通常是数组下标,然后把整个 键值对(代码中叫Entry)存放到这个位置,以加快查找的速度。如果多个 关键字(key) 得到同一位置就会产生 哈希冲突,需要通过一个方法去解决。
这里面有好几个概念,会在后面的内容去介绍。
🎄二、键值对(key-value pair)
键值对(key-value pair):就是一个关键字(key)对应一个值(value)的组合,要求每个键值对的 关键字(key) 不能重复。下面表格就是一些键值对。
Key | Value |
---|---|
xia | 虾 |
za | 匝 |
zai | 栽 |
zan | 赞 |
zang | 脏 |
在代码里可以使用下面结构体去表现一个键值对:
struct table_entry{
const char* key;
void *value;
}
哈希表是存储 键值对 的,那它是怎样存储的呢?
哈希表要求每个键值对的 关键字(key) 不能重复,然后通过键值对的 关键字(key) 来定位每个键值对存放的位置,这个位置就是 哈希地址,而将 关键字(key) 映射成 哈希地址 的函数就是 哈希函数。
在哈希表中,会把 键值对(key-value) 的key称为键值。
🎄三、哈希函数
哈希函数(Hash Function):也叫散列函数,将哈希表中元素的关键键值(key)映射为元素存储位置(哈希地址)的函数。
哈希函数是哈希表中最重要的部分。一般来说,哈希函数会满足以下几个条件:
- 哈希函数应该易于计算,并且尽量使计算出来的索引值均匀分布。
- 哈希函数计算得到的哈希值是一个固定长度的输出值。
- 计算出来的哈希地址不同,则传入键值(key)肯定不同;
Hash(key1) != Hash(key2),则 key1 != key2。
- 如果 Hash(key1) 等于 Hash(key2),那么 key1、key2 可能相等,也可能不相等(会发生哈希冲突)
在哈希表的应用中,最常见的2种键值类型是:字符串、数字。而其他键值类型还可以是是字符串类型、浮点数类型、大整数类型,甚至还有可能是几种类型的组合。设计哈希函数时,要根据键值类型的特点去设计,一般会先将键值转成整型再去计算,因为最终计算出来的一般是一个数组下标(Index)。
常见的哈希函数方法有:直接定址法、除留余数法、平方取中法、基数转换法、数字分析法、折叠法、随机数法、乘积法、点积法等。
假设我们把上个小节的键值对存到一个哈希表,并通过哈希函数计算出哈希地址,如下表:
Key | Value | 哈希地址(Index) |
---|---|---|
xia | 虾 | 517 |
za | 匝 | 597 |
zai | 栽 | 597 |
zan | 赞 | 599 |
zang | 脏 | 600 |
就像上面表格一样,哈希函数有时就将两个不同的键值计算出同一个哈希地址,这就造成了哈希冲突(哈希碰撞)
🎄四、哈希冲突(哈希碰撞)
哈希冲突(Hash Collision):不同的关键字通过同一个哈希函数可能得到同一哈希地址,即 key1 ≠ key2,而 Hash(key1) = Hash(key2),这种现象称为哈希冲突。
理想状态下,是每个key通过哈希函数都计算出不同的哈希地址,这样直接将键值对存入即可。但实际使用中一般都会出现哈希冲突的,这时就需要处理哈希冲突。处理哈希冲突的方法一般有两种:开放地址法、链地址法。
✨4.1 开发地址法
开放地址法(Open Addressing):指的是将哈希表中的 空地址 向处理冲突开放。当哈希表未满时,处理冲突时需要尝试另外的单元,直到找到空的单元为止。
开放地址法主要有三种实现:
- 线性探测(Linear Probing):发生冲突时,依次检查下一个地址,直到找到空闲位置。
- 二次探测(Quadratic Probing):冲突时,以二次函数的方式探查空闲位置。
- 双重哈希(Double Hashing):使用两个哈希函数,当发生冲突时,使用第二个哈希函数计算新的探查位置。
✨4.2 链地址法
链地址法(Chaining):将具有相同哈希地址的元素(或记录)存储在同一个线性链表中。
链地址法是一种更加常用的哈希冲突解决方法。相比于开放地址法,链地址法更加简单。
具体的做法是当哈希地址的位置是空的话,就直接将 键值对 存入该位置;如果不为空,就将 键值对 存到该地址的链表中,所以在代码实现时,会在键值对所在结构体中加一个next指针,用于实现链表。
struct TableEntry {
TableEntry* fNext;
char const* key;
void* value;
};
🎄五、哈希表的使用
虽然上面说了那么多的概念,但只是使用哈希表并不需要知道这些。如果是要看别人实现哈希表的代码,则必须要清楚上面的那些概念,这里再总结一下:
- 1、键值对(Key-Value Pair):是一种数据结构的表示形式,其中“键(Key)”是用于标识和查找数据的唯一标识符,“值(Value)”则是与该键相关联的数据。例如,在字典中,单词(键)与其释义(值)就构成了键值对。
- 2、哈希函数(Hash Function):是一种将输入(通常是键)转换为固定长度输出(称为哈希值)的函数。哈希函数的设计目标是使得不同的输入尽可能产生不同的输出,并且输出分布均匀。
- 3、哈希值(Hash Value):通过哈希函数对输入(键)进行计算得到的固定长度的数值。
- 4、哈希地址(Hash Address):哈希值所对应的存储位置。通常,哈希表会根据哈希值来确定数据在内存中的存储位置。
- 5、哈希冲突(Hash Collision):当两个或多个不同的键通过哈希函数计算得到相同的哈希值时,就发生了哈希冲突。这可能导致这些键对应的元素需要存储在同一个哈希地址,从而引发数据存储和检索的问题。
如果只是使用哈希表的话,只需要知道哈希表提供了哪些操作即可。哈希表至少会提供三个操作:插入、删除、查询。我们只需要知道怎样插入键值对、怎样删除、怎样查询就差不多了。
🎄六、总结
👉本文介绍哈希表实现过程中的重要概念:键值对、键值、哈希函数、哈希冲突、处理哈希冲突等,看完后可以对哈希表有一定的了解。
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁
参考:
https://blog.csdn.net/zy_dreamer/article/details/131036258
https://blog.csdn.net/sinat_33921105/article/details/103344078
https://blog.csdn.net/m0_65781965/article/details/136987203