代码随想录——力扣刷题笔记之哈希表理论

哈希表理论基础

 哈希表

        Hash table,是哈希表的英文(有些教材把它译为散列表)。

        哈希表听起来有点高大上,但是其实数组就是一张哈希表。而哈希表中的关键码就是数组的下表,通过数组下标我们就可以访问数组中的元素,具体结构如下图所示:

        那么一般什么情况下我们会想到用哈希表这个数据结构呢?一般我们都是用哈希表来快速判断一个元素是否出现在集合里。

        举个栗子:假设我们现在要判断一个人是否在一所学校里,这样的话我们就是要查找这个人的名字是不是在学校学生名单里面了。

        如果我们采用枚举法的话时间复杂度是O(n),但如果我们使用哈希表的话,只需要O(1)就可以做到。

        具体做法是:初始化的时候,我们可以把每个学生的名字都存在哈希表里,在进行查询操作的时候,通过索引我们就可以直接知道要找的这位同学在不在这所学校里了。

        那我们是如何将学生姓名映射到哈希表上的呢?这就涉及到了hash function,也就是哈希函数。

哈希函数

        接着用上面的例子。哈希函数就是把每个学生的姓名映射为哈希表的索引,然后我们通过查询索引的下标就可以快速的知道这位同学是否在这所学校里了。

        哈希函数的结构如下图所示。具体操作是这样的:通过hashcode将其他数据格式转化成不同的数值(一般hashcode是通过特定编码方式),这样一来就可以将学生的名字映射为哈希表上的索引数字了。

        当我们通过hashcode的方式得到的数值大于哈希表的大小,也就是tablesize了,那这时我们该怎么确保学生的名字一定能映射到哈希表上呢?

        此时,只要我们再次对数值进行一个取模操作,就能保证学生名字一定映射到哈希表上了。

        但是我们还要思考一个点,前面我们说到,所谓的哈希表其实就是一个数组,如果学生的数量大于哈希表的大小怎么办?这个时候就算哈希哈数计算得再均匀也会出现两个名字或者多个名字映射到同一个下标索引的情况。

        这就是哈希碰撞了。

哈希碰撞

        通过我们上面所提到的情况且结合下图,我们可以知道,小王和小李都映射到了同一位置这种现象就是我们所说的哈希碰撞

一般的哈希碰撞有两种常见的解决方法,一个叫拉链法,另一个叫线性探测法。

拉链法

        所谓拉链法,就是把发生冲突的元素像拉链一样存储在链表中。刚刚小李和小王在索引1的位置发生了冲突,也就是说在索引1的位置存在一个链表存储着小李和小王的名字,这样我们就可以通过索引直接找到小李和小王了。

        当拉链法选择合适的哈希表大小的时候,一来即不会因为数组空值而浪费大量内存空间,二来也不会因为链表太长而在查询时浪费太多时间。

线性探测法

        当我们使用线性探测法时,一定要保证tablesize要大于datasize。因为这个方法就是依靠哈希表中的空位来解决冲突问题。

        例如,小王和小李都被映射到了索引1的位置。当我们使用线性探测法的时候,我们可以选择将小李放在索引1的位置,然后在索引1下一个空的位置放置小王。

        由于这种解决方法,所以一定要求tablesize一定要大于datasize,要不然哈希表上就没有空位来存放发生冲突的数据了。具体如下图所示:

        哈希碰撞也是考研或者是本科期末考试里面数据结构与算法这门课里常考或者必考的内容,大家如果不理解的话,可以找些习题来练习一下就懂了。

常见的三种哈希结构(Python版本)

        当我们想用哈希法来解决问题的时候,一般有如下三种数据结构可供我们选择:

  • 数组(Arry)
  • 集合(Set)
  • 字典(Dictionary)

        数组的话就是跟前面介绍过的数组用法一样,这里就不再赘述了。

        介绍一下集合(Set)。

        集合(Set)是一种无序且不重复的数据结构,用于存储一组互不相同的元素。在大多数编程语言中,集合的实现通常基于哈希表或红黑树等数据结构。

        集合的特点包括:

  1. 无序性:集合中的元素没有特定的顺序,不像列表或数组那样按照索引排列。
  2. 不重复性:集合中的元素是唯一的,不会出现重复的元素。
  3. 快速查找:集合提供了高效的查找操作,可以在平均情况下以 O(1) 的时间复杂度确定一个元素是否存在于集合中。
  4. 添加和删除元素的高效性:集合支持快速添加和删除元素的操作。

        以下是一个使用Python的集合的示例:

# 创建集合
my_set = set()

# 添加元素
my_set.add(1)
my_set.add(2)
my_set.add(3)

# 删除元素
my_set.remove(2)

# 判断元素是否存在
if 1 in my_set:
    print("1 is in the set")

# 获取集合大小
size = len(my_set)
print("Size of the set:", size)

# 遍历集合
for item in my_set:
    print(item)

        然后介绍一下字典(Dictionary)。

        字典(Dictionary)是一种常见的数据结构,用于存储键-值对(key-value pairs)。在许多编程语言中,字典被称为哈希表(Hash Table)、关联数组(Associative Array)或映射(Map)等。

        字典的特点包括:

  1. 键-值对:字典中的每个元素都由一个键和一个对应的值组成。键是唯一的,它用于索引和访问对应的值。
  2. 无序性:字典中的元素没有特定的顺序,不像列表或数组那样按照索引排列。
  3. 快速访问:通过键可以快速查找和访问对应的值,通常具有接近 O(1) 的平均时间复杂度。
  4. 动态性:字典的大小可以动态地增加或减少,可以添加、更新或删除键-值对。

        字典在实际应用中非常常见,用于存储和管理具有唯一标识符(键)和相关数据(值)的信息。例如,可以使用字典来表示学生信息,其中每个学生的学号是键,对应的信息(如姓名、年龄、成绩等)是值。

        不同编程语言对字典的实现细节可能有所不同,但一般来说,字典的底层数据结构通常是哈希表,它能够提供高效的键-值对查找和访问操作。

        以下是一个使用Python的字典的示例:

# 创建字典
my_dict = {"apple": 3, "banana": 5, "orange": 2}

# 访问值
print(my_dict["apple"])  # 输出:3

# 更新值
my_dict["banana"] = 10

# 添加新键-值对
my_dict["grape"] = 7

# 删除键-值对
del my_dict["orange"]

# 遍历字典
for key, value in my_dict.items():
    print(key, value)

总结

        什么情况下会想到使用哈希法呢?就是当我们要快速判断某个元素是否出现在集合里的时候。

        虽然哈希法查询操作时间复杂度为O(1),但是这是牺牲了空间换来的。从上文的描述可知,我们都是用额外的数组或者集合或者字典来存放数据,才实现了快速查找。

  • 24
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值