数据结构(三)堆和哈希表
目录
- 哈希表和堆
- 什么是哈希表 ?
- 什么是堆 ?
- 什么是图 ?
- 案例一:使用python实现最小堆
- 案例二 : 如何用Python通过哈希表的方式完成商品库存管理
- 闯关题 (包含案例三:python实现哈希表)
本文是在原本数据结构与算法闯关的基础上总结得来,加入了自己的理解和部分习题讲解
原活动链接
邀请码: JL57F5
哈希表和堆
什么是哈希表 ?
哈希表存储的是由键(key)和值(value)组成的数据。例如,我们将每个人的性别作为数据进行存储,键为人名,值为对应的性别, 一般来说,我们可以把键当成数据的标识符,把值当成数据的内容。

为了对比一下哈希表的优势 , 我们先把这些数据存储到数组中看看效果

此处准备了6个箱子(即长度为6的数组)来存储数据。假设我们需要查询Ally的性别,由于不知道Ally的数据存储在哪个箱子里,所以只能从头开始查询。这个操作便叫作“线性查找”。 当我们查找到索引为4的时候, 才找到数据的键为Ally然后可以根据键把对应的值取出来
但是我们发现 , 数据量越多,线性查找耗费的时间就越长。由此可知:由于数据的查询较为耗时,所以此处并不适合使用数组来存储数据。但使用哈希表便可以解决这个问题。首先准备好数组,这次我们用5个箱子的数组来存储数据。
首先尝试把Joe存进去。注意 这个时候就不能把它放在所以为0的数组上了 要不然没啥意义 , 那怎么放, 通过什么方式呢 ? 这个我们涉及到用哈希函数(Hash)去进行操作。 使用哈希函数(Hash)计算Joe的键,也就是字符串“Joe”的哈希值 。 得到的结果为4928 ( 哈希函数可以把给定的数据转换成固定长度的无规律数值。 我们可以想象使数据更加的安全)
将得到的哈希值除以数组的长度5,求得其余数。这样的求余运算叫作“mod运算”。此处mod运算的结果为3。

同理 : Sue键的哈希值为7291, mod 5的结果为1,将Sue的数据存进1号箱中。

但是我们会发现, 如果余数都一样 , 冲突了怎么办 ? 比如 : Nell键的哈希值为6276, mod 5的结果为1。本应将其存进数组的1号箱中,但此时1号箱中已经存储了Sue的数据。这种存储位置重复了的情况便叫作“冲突”。

遇到这种情况,可使用链表在已有数据的后面继续存储新的数据, 这样我们如果查找Ally的性别该如何操作呢 ?
为了找到它的存储位置,先要算出Ally键的哈希值,再对其进行mod运算。最终得到的结果为3。于是我们找到了键为Ally的数据。取出其对应的值,便知道了Ally的性别为女(F)。

注意 : 在存储数据的过程中,如果发生冲突,可以利用链表在已有数据的后面插入新数据来解决冲突。这种方法被称为“链地址法”。比如这里的 1位置 和 3位置 都存在"冲突"
什么是堆 ?
堆是一种图的树形结构, 可以自由添加数据,但取出数据时要从最小值开始按顺序取出。在堆的树形结构中,各个顶点被称为“结点”(node),数据就存储在这些结点中。
什么是图 ?
那什么又是图呢 ? 说到“图”,我想可能大部分人想到的是散点图、柱状图,而计算机科学领域中说的“图”却是下面这样的。

上图中的圆圈叫作“顶点”(也可以叫“结点”),连接顶点的线叫作“边”。也就是说,由顶点和连接每对顶点的边所构成的图形就是图。
ok , 回到我们讲的堆

上图 , 这就是堆的例子 。结点内的数字就是它存储的数据。特别注意 : 堆中的每个结点最多有两个子结点。树的形状取决于数据的个数。另外,结点的排列顺序为从上到下,同一行里则为从左到右。
堆中存储数据时必须遵守这样的规则:
子结点必定大于父结点。因此,最小值被存储在顶端的根结点中。往堆中添加数据时,为了遵守这条规则,一般会把新数据放在最下面一行靠左的位置。当最下面一行里没有多余空间,就再往下另起一行,把数据加在这一行的最左端。所以说大家记住了吗 ?
ok, 我们来举个例子吧

我们试试往堆里添加数字5。如果放在6的右下角显然不符合堆的原则, 因为5小于6 , 按照规定必须是子节点大于父节点 , 那么此时 5 和 6调换一下位置就刚刚好 , 如果遇到同样的问题, 重复这样的操作直到数据都符合规则,不再需要交换为止。现在,父结点的1小于子结点的5,父结点的数字更小,所以不再交换。 因为 如果从堆中取出数据时,取出的是最上面的数据。这样,堆中就能始终保持最上面的数据最小, 需要注意的是 : 一旦最上面的数据被取出,因此堆的结构也需要根据原则进行重新调整。在此我们不过多赘述

堆中最顶端的数据始终最小,所以无论数据量有多少,取出最小值的时间复杂度都为O(1)
案例一:使用python实现最小堆
import heapqdef find_top_k_largest(nums,k):min_heap = []for num in nums:if len(min_heap)<k:heapq.heappush(min_heap,num)else:if num > min_heap[0]:heapq.heappop(min_heap)heapq.heappush(min_heap,num)return min_heap# return sorted(min_heap,reverse = True)# 示例用法
nums = [4,2,9,7,5,1,6,8,3]
k = 3
top_k_largest = find_top_k_largest(nums,k)
print(top_k_largest)
[7, 9, 8]
逐行解析代码:
import heapqdef find_top_k_largest(nums, k):min_heap = []
import heapq: 这行代码导入了 Python 的heapq模块,它提供了堆队列算法的实现,特别是最小堆。def find_top_k_largest(nums, k): 定义了一个函数find_top_k_largest,它接受两个参数:一个数组nums和一个整数k。min_heap = []: 初始化一个空列表min_heap,这个列表将被用作最小堆。
for num in nums:
for num in nums: 遍历数组nums中的每个元素,每个元素被引用为num。
if len(min_heap) < k:heapq.heappush(min_heap, num)
if len(min_heap) < k: 检查当前最小堆的大小是否小于k。heapq.heappush(min_heap, num): 如果是,则将当前元素num添加到最小堆中。
else:if num > min_heap[0]:heapq.heappop(min_heap)heapq.heappush(min_heap, num)
else: 如果最小堆的大小已经达到k。if num > min_heap[0]: 检查当前元素num是否大于最小堆的顶部元素(即最小元素)。heapq.heappop(min_heap): 如果是,首先从最小堆中移除顶部元素(最小元素)。heapq.heappush(min_heap, num): 然后将当前元素num加入到最小堆中。
return min_heap
return min_heap: 函数返回最终构建的最小堆,这个堆包含了数组nums中的前k个最大元素。
在您的示例用法中,这个函数被用来找出数组 [4, 2, 9, 7, 5, 1, 6, 8, 3] 中的前 3 个最大元素,最终结果为 [7, 8, 9]。这个方法是通过维护一个大小为 k 的最小堆实现的,它确保了堆中始终保持数组中的前 k 个最大元素。
- 提问:输出的顺序是[7,9,8] 非 [7,8,9]
您提到的输出 [7, 9, 8] 实际上也是正确的。这个结果出现的原因在于堆不保证元素的有序性,尤其是在非根部的位置。在您的例子中,最小堆确实维护了数组中的前 k 个最大元素,但是这些元素在堆中的顺序并不是有序的。
在最小堆中,只有根部(即堆顶元素,min_heap[0])是保证最小的,其他位置的元素并没有特定的顺序。当您提取出堆中的元素时,它们的顺序依赖于堆在内部的具体排列,这个排列是由 heapq 模块在执行插入和删除操作时决定的。
如果您需要一个有序的结果,您可以在返回前对堆中的元素进行排序。例如:
return sorted(min_heap, reverse=True)
这将确保返回的列表是按照从大到小的顺序排列的。但请注意,这个排序步骤会增加额外的时间复杂度。如果您不关心结果的顺序,那么直接返回未排序的堆就足够了。
案例二 : 如何用Python通过哈希表的方式完成商品库存管理
背景:
在一个电商系统中,我们需要管理商品的库存信息。每个商品都有一个唯一的商品编号(item_id)和对应的库存数量(stock)。当用户下单购买商品时,需要实时检查商品的库存情况,以确保有足够的库存供应。
任务:
请实现一个基于哈希表(字典)的商品库存管理系统。具体要求如下:
定义一个函数 add_stock(item_id, quantity),用于向库存系统中添加商品库存。如果商品已存在于系统中,则将库存数量累加;如果商品还不存在于系统中,则添加新的商品及其库存信息。
定义一个函数 subtract_stock(item_id, quantity),用于从库存系统中减少商品库存。如果商品不存在于系统中,则抛出异常;如果商品库存不足以满足要求的减少量,则抛出异常;否则,更新商品的库存数量。
定义一个函数 get_stock(item_id),用于获取指定商品的库存数量。
# 商品库存管理系统
stock_dict = {} # 创建一个字典作为商品库存表def add_stock(item_id, quantity):"""向库存系统中添加商品库存如果商品已存在于系统中,则将库存数量累加;如果商品还不存在于系统中,则添加新的商品及其库存信息。"""if item_id in stock_dict:stock_dict[item_id] += quantityelse:stock_dict[item_id] = quantitydef subtract_stock(item_id, quantity):"""从库存系统中减少商品库存如果商品不存在于系统中,则抛出异常;如果商品库存不足以满足要求的减少量,则抛出异常;否则,更新商品的库存数量。"""if item_id not in stock_dict:raise Exception("Item does not exist in stock")if stock_dict[item_id] < quantity:raise Exception("Insufficient stock")stock_dict[item_id] -= quantitydef get_stock(item_id):"""获取指定商品的库存数量"""return stock_dict.get(item_id, 0)# 示例演示
add_stock("item001", 100) # 添加商品 "item001",库存数量为 100
add_stock("item002", 50) # 添加商品 "item002",库存数量为 50print("Current stock:")
print(stock_dict) # 打印当前商品库存情况subtract_stock("item001", 20) # 减少商品 "item001" 库存 20
stock = get_stock("item001") # 获取商品 "item001" 的库存
print("Current stock:", stock)subtract_stock("item002", 70) # 尝试减少商品 "item002" 库存 70(超过实际库存量)
# 库存不足异常将被抛出,程序终止运行
Current stock:
{'item001': 100, 'item002': 50}
Current stock: 80---------------------------------------------------------------------------Exception Traceback (most recent call last)<ipython-input-1-3a5c254f3c86> in <module>45 print("Current stock:", stock)46
---> 47 subtract_stock("item002", 70) # 尝试减少商品 "item002" 库存 70(超过实际库存量)48 # 库存不足异常将被抛出,程序终止运行<ipython-input-1-3a5c254f3c86> in subtract_stock(item_id, quantity)24 25 if stock_dict[item_id] < quantity:
---> 26 raise Exception("Insufficient stock")27 28 stock_dict[item_id] -= quantityException: Insufficient stock
在以上代码示例中,我们创建了一个名为 stock_dict 的字典,用于存储商品库存信息。通过 add_stock 函数向库存系统中添加商品库存,通过 subtract_stock 函数减少商品库存,通过 get_stock 函数获取指定商品的库存数量。在函数实现上,我们利用字典的键值对特性,将商品编号作为键,库存数量作为对应的值进行存储和访问。
在主程序中,我们先添加了两个商品的库存信息,然后演示了减少库存和获取库存的操作。在减少库存时,如果库存不足或商品不存在,将会抛出相应的异常信息。
闯关题 (包含案例三:python实现哈希表)
STEP1:根据要求完成题目
Q1.(单选) 一个大小为n的数组中,可以快速找到前k大的数,应该使用哪种数据结构?
A. 数组
B. 链表
C. 栈
D. 堆
E. 哈希表
Q2.(单选)以下哪一组操作不是哈希表的基本操作?
A. 插入
B. 删除
C. 清空
D. 查找
E. 排序
Q3.(判断对错)堆中的每个结点最多有两个子结点, 这两个节点要求是所有结点中最大的 (T/F)
Q4.(判断对错)结点的排列顺序为从上到下,同一行里则为从左到右 (T/F)
使用 Python 实现一个哈希表,要求具有以下方法:
- set(key, value):将键值对(key, value)插入哈希表中,如果 key 已经存在,则覆盖其原有的值
- get(key):返回哈希表中指定 key 的值,如果 key 不存在,则返回 None
- delete(key):从哈希表中删除指定 key 的键值对
提示:
- 可以使用 Python 内置的字典 dict 来实现哈希表
- 在 set 和 delete 方法中,要注意先检查字典中是否存在该 key
class HashTable:# 定义哈希表类,使用 Python 内置的 dict 实现def __init__(self):self.table = {}def set(self, key, value):"""向哈希表中插入键值对"""#题目q5 : 向哈希表中插入键值对self.table[key] = valuedef get(self, key):"""获取指定 key 对应的 value"""if key in self.table:# 题目q6 :返回指定的键对应的值return self.table[key]else:return Nonedef delete(self, key):"""从哈希表中删除指定的键值对"""if key in self.table:del self.table[key]
观察上面的代码,完成下面的单选题(注意查看前后代码)
Q5. 代码第11行为空,现在需要实现向哈希表中插入键值对,下面哪个选项为正确代码,选择正确选项并把结果赋值给a5
A : self.table[key] = value
B : table[key] = value
C : self.table = {}
D : self.table[key] = {key : value}
Q6. 代码第19行为空,现在需要实现返回指定的键对应的值,下面哪个选项为正确代码,选择正确选项并把结果赋值给a6
A : return table[key]
B : return self.table[key]
C : return value
D : return {value}
#填入你的答案
a1 = 'D' # 如 a1 = 'A'
a2 = 'E' # 如 a2 = 'A'
a3 = 'F' # 如 a3 = 'T'
a4 = 'T' # 如 a4 = 'T'
a5 = 'A' # 如 a5 = 'C'
a6 = 'B' # 如 a6 = 'A'
STEP2:将结果保存为 csv 文件
csv 需要有两列,列名:id、answer。其中,id 列为题号,如 q1、q2;answer 列为 STEP1 中各题你计算出来的结果。💡 这一步的代码你无需修改,直接运行即可。
# 生成 csv 作业答案文件
def save_csv(a1, a2, a3, a4, a5,a6) : import pandas as pddf = pd.DataFrame({"id": ["q1", "q2", "q3", "q4","q5","q6"], "answer": [a1, a2, a3,a4,a5,a6]})df.to_csv("answer_ago_1_3.csv", index=None)save_csv(a1, a2, a3, a4, a5,a6) # 运行这个cell,生成答案文件;该文件在左侧文件树project工作区下,你可以自行右击下载或者读取查看
相关文章:
数据结构(三)堆和哈希表
目录 哈希表和堆什么是哈希表 ?什么是堆 ?什么是图 ?案例一:使用python实现最小堆案例二 : 如何用Python通过哈希表的方式完成商品库存管理闯关题 (包含案例三:python实现哈希表) 本…...
李宏毅LLM——ChatGPT原理剖析
文章目录 Chat-GPT引言关键技术——预训练研究问题玩文字冒险游戏 ChatGPT原理剖析 Chat-GPT引言 直观感受:结果有模有样、每次输出结果都不同、可以追问、幻想出的答案误解:罐头回答、答案是网络搜索的结果真正做的事:文字接龙,…...
让Windows上vscode的C语言scanf函数可以读取中文字符
windows的默认字符集保存为GBK不要修改 区域设置–时钟和区域–区域–管理–更系统区域设置–(不要勾选)使用UTF-8。 查看验证当前字符集: cmdchcp 活动代码页: 936936就是简体中文GBK vscode的setting.json文件添加如下代码 点击左下角…...
Linux命令(3)
一. tr 对字符进行处理: tr 命令用于字符转换、替换和删除,主要用于删除文件中的控制符或进行字符串转换等。 将a转换成1 将小写字母转换成大写 压缩: tr -s 将a压缩成一个a 将空格压缩成一个 删除: tr -d 补集: 用字符串中的字符集的补…...
安卓MediaRecorder(3)音频采集编码写入详细源码分析
文章目录 前言音频采集音频初始化AudioRecord 分析AudioSource 采集到音频 音频编码音频编码后数据处理MPEG4Writer写入音频编码后数据到文件MPEG4Writer::Track 取编码后的音频编数据结语 本文首发地址 https://blog.csdn.net/CSqingchen/article/details/134896808 最新更新地…...
2024年网络安全竞赛—网络安全事件分析应急响应解析(包含FLAG)
网络安全事件分析应急响应 目录 网络安全事件分析应急响应 解析如下:...
FineBI实战项目一(22):各省份订单个数及订单总额分析开发
点击新建组件,创建各省份订单个数及订单总额组件。 选择自定义图表,将province拖拽到横轴,将cnt和total拖拽到纵轴。 调节纵轴的为指标并列。 修改横轴和纵轴的标题。 修改柱状图样式: 将组件拖拽到仪表板。 结果如下:…...
2024.1.16 调用tinyspline样条曲线拟合库时报 stack smashing detected,CMakeLists.txt中屏蔽该异常
在函数中调用第三方库api拟合样条曲线,函数中一切正常,可以打印所有数组变量,重复执行该函数,某一次函数return时报 stack smashing deteced (unknown) ,原因可能是第三方库内部的函数有栈溢出风…...
Leetcode202快乐数(java实现)
今天分享的题目是快乐数: 快乐数的定义如下: 快乐数(Happy Number)是指一个正整数,将其替换为各个位上数字的平方和,重复这个过程直到最后得到的结果为1,或者无限循环但不包含1。如果最终结果为…...
50天精通Golang(第13天)
反射reflect 一、引入 先看官方Doc中Rob Pike给出的关于反射的定义: Reflection in computing is the ability of a program to examine its own structure, particularly through types; it’s a form of metaprogramming. It’s also a great source of confus…...
大数据 - Doris系列《三》- 数据表设计之表的基本概念
目录 🐶3.1 字段类型 🐶3.2 表的基本概念 3.2.1 Row & Column 3.2.2 分区与分桶 🥙3.2.2.1 Partition 1. Range 分区 2. List 分区 进阶:复合分区与单分区的选择 3.2.3 PROPERTIES 🥙3.2.3.1 分片副本数 …...
数据库mysql no.3
1.排序查询 order by 排序列表 【asc/desc】 排序列表:可以是单个字段、多个字段、表达式、函数、别名。 asc 升序 desc 降序 如果没有写那就是默认升序 2.常见函数 select 函数名(); 定义:函…...
数据结构实战:变位词侦测
文章目录 一、实战概述二、实战步骤(一)逐个比较法1、编写源程序2、代码解释说明(1)函数逻辑解释(2)主程序部分 3、运行程序,查看结果4、计算时间复杂度 (二)排序比较法1…...
C++核心编程之类和对象---C++面向对象的三大特性--多态
目录 一、多态 1. 多态的概念 2.多态的分类: 1. 静态多态: 2. 动态多态: 3.静态多态和动态多态的区别: 4.动态多态需要满足的条件: 4.1重写的概念: 4.2动态多态的调用: 二、多态 三、多…...
基于PyQT的图片批处理系统
项目背景: 随着数字摄影技术的普及,人们拍摄和处理大量图片的需求也越来越高。为了提高效率,开发一个基于 PyQt 的图片批处理系统是很有意义的。该系统可以提供一系列图像增强、滤波、水印、翻转、放大缩小、旋转等功能,使用户能够…...
vscode文件配置
lanuch.json {"version": "0.2.0","configurations": [{"name": "(gdb) 启动","type": "cppdbg","request": "launch",// "program": "输入程序名称,例…...
C++学习笔记——SLT六大组件及头文件
目录 一、C中STL(Standard Template Library) 二、 Gun源代码开发精神 三、 实现版本 四、GNU C库的头文件分布 bits目录 ext目录 backward目录 iostream目录 stdexcept目录 string目录 上一篇文章: C标准模板库(STL&am…...
Spring之AOP源码(二)
书接上文 文章目录 一、简介1. 前文回顾2. 知识点补充 二、ProxyFactory源码分析1. ProxyFactory2. JdkDynamicAopProxy3. ObjenesisCglibAopProxy 三、 Spring AOP源码分析 一、简介 1. 前文回顾 前面我们已经介绍了AOP的基本使用方法以及基本原理,但是还没有涉…...
VS code console.log快捷键设置 :console.log(‘n>>>‘,n)
vscode设置log快捷显示: 一、打开 VS Code,并进入菜单栏选择 “文件”(File)-> “首选项”(Preferences)-> “用户代码片段”(User Snippets)。 二、在弹出的下拉菜单中选择 …...
ZooKeeper 简介
1、概念介绍 ZooKeeper 是一个开放源码的分布式应用程序协调服务,为分布式应用提供一致性服务的软件,由雅虎创建,是 Google Chubby 的开源实现,是 Apache 的子项目,之前是 Hadoop 项目的一部分,使用 Java …...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
