【Python 数据结构 15.哈希表】

目录

一、哈希表的基本概念

1.哈希表的概念

2.键值对的概念

3.哈希函数的概念

4.哈希冲突的概念

5.常用的哈希函数

Ⅰ、直接定址法

Ⅱ、平方取中法

Ⅲ、折叠法

Ⅳ、除留余数法

Ⅴ、位与法

6.哈希冲突的解决方案

Ⅰ、开放定址法

Ⅱ、链地址法

7.哈希表的初始化

8.哈希表的元素插入

9.哈希表的元素删除

10.哈希表的元素查找

二、Python中的哈希表

1.哈希表的创建(字典)

2.哈希表的元素修改

Ⅰ、元素的索引

Ⅱ、元素的添加

Ⅲ、元素的删除

Ⅳ、元素的修改

3.哈希表的查找与遍历

Ⅰ、通过键查找值

Ⅱ、遍历哈希表的键

Ⅲ、遍历哈希表的值

Ⅳ、遍历哈希表的键和值 

三、代码实战

1512. 好数对的数目

方法一、哈希表

思路与算法

方法二、二重循环

思路与算法

961. 在长度 2N 的数组中找出重复 N 次的元素

方法一、哈希表

思路与算法

​编辑

1207. 独一无二的出现次数

方法一 哈希表

思路与算法


你每次都会自己站起来,这次又怎会是意外

                                                                —— 25.3.13

一、哈希表的基本概念

1.哈希表的概念

        哈希表又叫散列表,我们需要把查找的数据通过一个函数映射,找到存储数据的位置,这个过程被称为哈希。需要查找的数据本身被称为关键字,通过一个函数映射将关键字变成哈希值的过程,这里的函数被称为哈希函数

        生成哈希值的过程可能产生冲突(两个关键字通过一个哈希函数后得到的哈希值相同),需要进行冲突解决,解决完冲突以后,实际存储数据的位置被称为哈希地址。通俗的说,它就是一个数组下标存储所有这些数据的表,就被称为哈希表

        为了方便索引,哈希表底层实现结构是一个顺序表,每个位置被称为一个槽,存储一个键值对。以下就是一个长度为 8 的哈希表:


2.键值对的概念

        键值对由组成,键和值都可以是任意类型(比如整型、浮点型、字符串、类 等等)。

        哈希表的实现过程中,我们需要通过一些手段将一个非整型的键转换成整数,也就是哈希值,从而通过 O(1) 的时间快速索引到它对应在哈希表中的位置。而将一个非整形的关键字转换成整型的手段,就是哈希函数


3.哈希函数的概念

        哈希函数可以理解为小学课本上的那个函数 y=f(x),这里的 f(x) 就是哈希函数。x 是键,y 是哈希值。好的哈希函数应该具备两个特征:(1)单射 (2)雪崩效应

        单射:哈希值 y 与 键 x 一一对应

        雪崩效应:为了让哈希值,更加符合随机分布的原则,哈希表中的键分布的越随机,利用率越高,效率也越高。


4.哈希冲突的概念

        哈希函数在生成哈希值的过程中,如果不同的关键字传入哈希函数后得到相同的哈希值,就被称为 哈希冲突


5.常用的哈希函数

Ⅰ、直接定址法

        直接定址法就是:键本身就是哈希值,表示成函数就是 f(x) = x,例如计数排序的原理,采用的就是直接定址法。由于哈希值是需要映射到顺序表中作为索引的,所以直接定址法只能处理数据量较小的且为非负整数的键。

Ⅱ、平方取中法

        平方取中法就是对键进行平方运算,再取中间的某几位作为哈希值例如:对于键 1314 平方后得到 1726596,取中间三位作为哈希值,即 265。平方取中法比较适合于不清楚键的分布,且位数不是很大的情况。

Ⅲ、折叠法

        折叠法是将关键字分割成位数相等的几部分,然后再进行求和,得到一个哈希值。例如:对于关键字 5201314,将它分为四组,并且相加得到52+01+31+4=88,这个就是哈希值。

Ⅳ、除留余数法

        除留余数法,就是 键的值上哈希表长度,表示成函数 f(x) = x mod m,其中 m 代表了哈希表的长度。这种方法,不仅可以对关键字取模,也可以在平方取中法、折叠法之后再取模。

        例如:对于一个长度为 4 的哈希表,可以将关键字 模4 得到哈希值。而这个方法也是我们要重点介绍的方法。

Ⅴ、位与法

        哈希表的长度一般选择 2 的幂

        取模运算比较耗时,而位运算相对较高效,选择 2 的幂作为哈希表长度,可以将取模运算 转换成 二进制位与,令 m 等于 2 的 k 次,其二进制表示为:

        任何一个数模上 m,相当于取了 m 的二进制的低 k 位:

        m 的模运算 与 m - 1 的位于运算效果是一样的:x % S == x & (S - 1)

        除了直接定址法,其他方法都可能导致哈希冲突


6.哈希冲突的解决方案

        解决哈希冲突的主要两种方法:开放定址法链地址法,无论是开放地址法,还是链地址法,都可以实现哈希表,我们只需要选择其中一种即可。

Ⅰ、开放定址法

        开放定址法就是一旦发生冲突,就去寻找下一个空的地址,只要哈希表足够大,总能找到一空的位置,并且记录下来作为它的哈希值,公式:

d_i是一个数列,可以是常数列,也可以是等差数列

        哈希表的每个数据就是一个键,插入之前需要先进行查找,如果找到的位置未被插入则执行插入,否则找到下一个未被插入的位置进行插入。

        这种方法需要注意的是:当插入数据超过哈希表长度时,不能再执行插入,否则会造成死循环。

Ⅱ、链地址法

        当产生冲突后,我们也可以选择不换位置,还是在原来的位置,只是把 哈希值 相同的用链表串联起来,这种方法被称为链地址法。

        哈希表的每个数据,保留了链表头结点和尾结点,插入前需要先进行查找,如果找到的位置链表非空,则插入尾结点,并且更新尾结点。否则生成一个新的链表头结点和尾结点。


7.哈希表的初始化

        给定一个大小 n,申请一个 n 个元素的数组,元素类型是:哈希表键值对


8.哈希表的元素插入

     给定元素,利用哈希函数计算它的哈希值,对数组长度 n 取模以后,找到合适的位置,遍历这个位置上的链表,如果发现没有键值对相等的元素,则插入这个链表   


9.哈希表的元素删除

        给定元素,利用哈希函数计算它的哈希值,对数组长度 n 取模以后,找到合适的位置,遍历这个位置上的链表,如果发现有键值对相等的元素,则从链表上进行删除


10.哈希表的元素查找

        给定元素,利用哈希函数计算它的哈希值,对数组长度 n 取模以后,找到合适的位置,遍历这个位置上的链表,如果发现键值对相等的元素,返回 True;否则,返回 False。


二、Python中的哈希表

1.哈希表的创建(字典)

hash = {}
hash = {"e":3, "t":6, "a":1, "o":4, "i":5, "n":2}
print(hash)


2.哈希表的元素修改

Ⅰ、元素的索引

hash['u'] = 9
print(hash)hash['u'] = 4
print(hash)


Ⅱ、元素的添加

hash['z'] = 13
print(hash)


Ⅲ、元素的删除

hash.pop():用于移除字典中指定键的项,并返回该键对应的值。如果指定的键不存在,可提供一个默认值,否则会引发 KeyError 异常。

参数名类型是否必需描述
key任意可哈希类型需要从字典中移除的键
default任意类型如果指定的键不存在时返回的默认值,默认为 None
hash.pop('o')
print(hash)


Ⅳ、元素的修改

hash['z'] += 1print(hash['z'])


3.哈希表的查找与遍历

Ⅰ、通过键查找值

hash.get():用于返回指定键的值。如果键存在于字典中,则返回对应的值;如果键不存在,则返回默认值(默认为 None),不会引发 KeyError 异常。

hash.get(x, 0) + 1:通过遍历列表 nums,使用哈希表(字典)hash 可以统计每个元素的出现次数。

参数名类型是否必需描述
key任意可哈希类型需要查找值的键
default任意类型如果指定的键不存在时返回的默认值,默认为 None
print(hash.get('x', 9))

Ⅱ、遍历哈希表的键

hash.keys():返回一个视图对象,该对象包含了字典中所有的键。

for k in hash.keys():print(k, end = " ")


Ⅲ、遍历哈希表的值

hash.values(): 返回一个视图对象,该对象包含了字典中所有的值。

for v in hash.values():print(v, end = " ")


Ⅳ、遍历哈希表的键和值 

hash.items():返回一个视图对象,该对象包含了字典中所有的键值对,每个键值对以元组的形式表示。

for k,v in hash.items():print(k, v, end = " ")


三、代码实战

1512. 好数对的数目

给你一个整数数组 nums 。

如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,就可以认为这是一组 好数对 。

返回好数对的数目。

示例 1:

输入:nums = [1,2,3,1,1,3]
输出:4
解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始

示例 2:

输入:nums = [1,1,1,1]
输出:6
解释:数组中的每组数字都是好数对

示例 3:

输入:nums = [1,2,3]
输出:0

提示:

  • 1 <= nums.length <= 100
  • 1 <= nums[i] <= 100

方法一、哈希表

思路与算法
  1. 哈希表记录频次:使用字典(哈希表)hash 记录每个元素已出现的次数。
  2. 累加配对数:遍历数组时,对于当前元素 x,检查它之前已出现的次数 hash.get(x, 0),将这些次数累加到 count 中(每次出现的新元素会与之前所有相同元素形成新对)。
  3. 动态更新频次:将当前元素 x 的出现次数加 1,更新到哈希表中。

关键点:每个元素 x 在遍历时,仅统计它之前出现的次数,从而避免重复计数。

class Solution:def numIdenticalPairs(self, nums: List[int]) -> int:hash = {}n = len(nums)count = 0for i in range(n):x = nums[i]count += hash.get(x, 0)hash[x] = hash.get(x, 0) + 1return count


方法二、二重循环

思路与算法

此方法为暴力解法,通过双重循环遍历数组中所有可能的索引对 (i, j)(其中 i < j),直接比较元素是否相等。若相等则计数器 count 加 1。

关键点

  • 时间复杂度高,但逻辑简单直观。
  • 适用于小规模数据,但对大规模数据效率极低。
class Solution:def numIdenticalPairs(self, nums: List[int]) -> int:n = len(nums)count = 0for i in range(n):for j in range(i + 1, n):if nums[i] == nums[j]:count += 1return count


961. 在长度 2N 的数组中找出重复 N 次的元素

给你一个整数数组 nums ,该数组具有以下属性:

  • nums.length == 2 * n.
  • nums 包含 n + 1 个 不同的 元素
  • nums 中恰有一个元素重复 n 次

找出并返回重复了 n 次的那个元素。

示例 1:

输入:nums = [1,2,3,3]
输出:3

示例 2:

输入:nums = [2,1,2,5,3,2]
输出:2

示例 3:

输入:nums = [5,1,5,2,5,3,5,4]
输出:5

提示:

  • 2 <= n <= 5000
  • nums.length == 2 * n
  • 0 <= nums[i] <= 104
  • nums 由 n + 1 个 不同的 元素组成,且其中一个元素恰好重复 n 次

方法一、哈希表

思路与算法

① 遍历列表统计列表中元素及其出现次数

② 遍历哈希表,找到频次等于 n // 2的元素

class Solution:def repeatedNTimes(self, nums: List[int]) -> int:hash = {}for x in nums:hash[x] = hash.get(x, 0) + 1for k, v in hash.items():if v == len(nums) // 2:return k


1207. 独一无二的出现次数

给你一个整数数组 arr,如果每个数的出现次数都是独一无二的,就返回 true;否则返回 false

示例 1:

输入:arr = [1,2,2,1,1,3]
输出:true
解释:在该数组中,1 出现了 3 次,2 出现了 2 次,3 只出现了 1 次。没有两个数的出现次数相同。

示例 2:

输入:arr = [1,2]
输出:false

示例 3:

输入:arr = [-3,0,1,-3,1,1,1,-3,10,0]
输出:true

提示:

  • 1 <= arr.length <= 1000
  • -1000 <= arr[i] <= 1000

方法一 哈希表

思路与算法

统计频次:使用字典 count 统计每个元素的出现次数。

检查重复:使用字典 hash 记录已出现的次数值。若某次数的值已存在,则说明有重复,直接返回 False;否则继续遍历。

最终判定:若所有次数均无重复,返回 True

将测试用例代入哈希表流程进行实验:

class Solution:def uniqueOccurrences(self, arr: List[int]) -> bool:count = {}# arr = [1,2,2,1,1,3]for i in arr:# count[1] = 1# count[2] = 1# count[2] = 2# count[1] = 2# count[1] = 3# count[3] = 1count[i] = count.get(i, 0) + 1hash = {}for i in count.values():# 1:3 2:2 3:1if hash.get(i):# hash = {}             hash = {3:1}            hash = {3:1, 2:1}# hash.get(3) = None    hash.get(2) = None      hash.get(1) = Nonereturn False# hash[3] = 1           hash[2] = 1             hash[1] = 1hash[i] = 1return True     # 遍历完成,返回True

 

class Solution:def uniqueOccurrences(self, arr: List[int]) -> bool:count = {}# arr = [1,2]for i in arr:# count[1] = 1# count[2] = 1count[i] = count.get(i, 0) + 1hash = {}for i in count.values():# 1:1 2:1# hash = {}         hash = {1:1}if hash.get(i):   # hash.get(1) = Truereturn False  # return False  # hash[1] = 1hash[i] = 1return True     # 遍历完成,返回True
解题代码
class Solution:def uniqueOccurrences(self, arr: List[int]) -> bool:count = {}for i in arr:   count[i] = count.get(i, 0) + 1        hash = {}for v in count.values():if hash.get(v):return Falsehash[v] = 1return True

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/33934.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试之测试分类

1. 为什么要对软件测试进行分类 软件测试是软件⽣命周期中的⼀个重要环节&#xff0c;具有较⾼的复杂性&#xff0c;对于软件测试&#xff0c;可以从不同的⻆度 加以分类&#xff0c;使开发者在软件开发过程中的不同层次、不同阶段对测试⼯作进⾏更好的执⾏和管理测试 的分类⽅…

Devops CI/CD

Devops CI/CD DevOps 中的 CI/CD&#xff1a;持续集成与持续部署的深度解析一、CI/CD 基本概念&#xff08;一&#xff09;持续集成&#xff08;二&#xff09;持续部署 二、CI/CD 实施步骤&#xff08;一&#xff09;版本控制&#xff08;二&#xff09;自动化构建&#xff08…

leetcode105为什么可以root.left可以截取到前序遍历二叉树的(0,index),而不是(1,index+1)

这里以105前序和中序遍历构造二叉树为例&#xff0c;106同理 原因在于preoder.shift()会改变原数组&#xff0c;已经把preoder的第一个队头元素已经排除出去了&#xff01;&#xff01;&#xff01; 306题中的截取后续遍历中用pop&#xff08;&#xff09;同理

数据结构---堆栈和列

一、堆栈 1.栈堆&#xff1a;具有一定操作约束的线性表&#xff1b;&#xff08;只在一端做插入删除&#xff09; 2.栈的顺序存储结构&#xff1a; 由一个一维数组和一个记录栈顶元素位置的变量组成。定义方式如下&#xff1a; 3.入栈操作&#xff1a; 注意&#xff1a;&…

golang快速上手基础语法

变量 第一种&#xff0c;指定变量类型&#xff0c;声明后若不赋值&#xff0c;使用默认值0 package mainimport "fmt"func main() {var a int //第一种&#xff0c;指定变量类型&#xff0c;声明后若不赋值&#xff0c;使用默认值0。fmt.Printf(" a %d\n"…

【idea代码ai插件】利用接入硅基流动的deepseekR1的api在idea里实现问答,辅助写代码

注册硅基流动账号 https://siliconflow.cn/zh-cn/ 然后新建api密钥&#xff0c;这里的api密钥可以点击复制&#xff0c;等会输入要用 可以看到现在新注册是有额度的&#xff0c;你们应该是14元 模型广场这里可以调用deepseek的v3和r1&#xff0c;注意因为是蹭&#xff0c;赠…

NO.42十六届蓝桥杯备战|数据结构|算法|时间复杂度|空间复杂度|STL(C++)

数据结构 什么是数据结构 在计算机科学中&#xff0c;数据结构是⼀种数据组织、管理和存储的格式。它是相互之间存在⼀种或多种特定关系的数据元素的集合。 说点通俗易懂的话&#xff0c;数据结构就是数据的组织形式&#xff0c;研究的就是把数据按照何种形式存储在计算机中 …

【CSS3】化神篇

目录 平面转换平移旋转改变旋转原点多重转换缩放倾斜 渐变线性渐变径向渐变 空间转换平移视距旋转立体呈现缩放 动画使现步骤animation 复合属性animation 属性拆分逐帧动画多组动画 平面转换 作用&#xff1a;为元素添加动态效果&#xff0c;一般与过渡配合使用 概念&#x…

Keepalived高可用架构实战:从安装配置到高级应用详解

一.架构 用户空间核心组件&#xff1a; vrrp stack&#xff1a;VIP 消息通信checkers&#xff1a;监测 Real Serversystem call&#xff1a;实现 vrrp 协议状态转换时调用相关本地功能SMTP&#xff1a;邮件组件IPVS wrapper&#xff1a;生成 IPVS 规则Netlink Reflector&…

Linux:利用System V系列的-共享内存,消息队列实现进程间通信

对于管道的进程间通信方式&#xff0c;需要频繁的调用系统调用(read,write)。而我们今天首先要介绍的共享内存&#xff0c;在开辟好空间之后&#xff0c;便可以跳过系统调用&#xff0c;直接进行读写操作。 一.System V共享内存(主要) 共享内存区是最快的IPC形式。一旦这样的内…

不像人做的题————十四届蓝桥杯省赛真题解析(上)A,B,C,D题解析

题目A&#xff1a;日期统计 思路分析&#xff1a; 本题的题目比较繁琐&#xff0c;我们采用暴力加DFS剪枝的方式去做&#xff0c;我们在DFS中按照8位日期的每一个位的要求进行初步剪枝找出所有的八位子串&#xff0c;但是还是会存在19月的情况&#xff0c;为此还需要在CHECK函数…

宇树人形机器人开源模型

1. 下载源码 https://github.com/unitreerobotics/unitree_ros.git2. 启动Gazebo roslaunch h1_description gazebo.launch3. 仿真效果 H1 GO2 B2 Laikago Z1 4. VMware: vmw_ioctl_command error Invalid argument 这个错误通常出现在虚拟机环境中运行需要OpenGL支持的应用…

【C/C++算法】从浅到深学习--- 前缀和算法(图文兼备 + 源码详解)

绪论&#xff1a;冲击蓝桥杯一起加油&#xff01;&#xff01; 每日激励&#xff1a;“不设限和自我肯定的心态&#xff1a;I can do all things。 — Stephen Curry” 绪论​&#xff1a; 本章将使用八道题由浅到深的带你了解并基本掌握前缀和思想&#xff0c;以及前缀和的基…

脑电:时域分析(任务态)

时域分析&#xff1a;时间序列&#xff08;时域信号&#xff09; EEG和ERP都是时间序列 ERP&#xff1a;事件诱发的电位是随着时间变化 组水平&#xff1a;需要这一组的个体不能差异性太大。 提值的指标&#xff0c;选取平均幅值确定成分的显著情况 mean(EEG.data,3): 在第…

【C语言】自定义类型:结构体,联合,枚举(下)

前言&#xff1b;上一期我们侧重讲了一个非常重要的自定义类型结构体&#xff0c;这一期我们来说说另外两种自定义类型&#xff1a;联合&#xff0c;和枚举。 传送门&#xff1a;自定义类型&#xff1a;结构体&#xff0c;联合&#xff0c;枚举(上) 文章目录 一&#xff0c;联…

数组的介绍

1.数组的概念 数组是一组相同类型元素的集合&#xff0c;从这个描述中我们知道&#xff1a; 数组中存放1个或多个数据&#xff0c;但是数组的元素个数不为0。数组中存放的多个数据&#xff0c;类型是相同的。 数组分为一维数组和多维数组&#xff0c;多维数组一般比较多见的…

蓝桥杯 17110抓娃娃

问题描述 小明拿了 n 条线段练习抓娃娃。他将所有线段铺在数轴上&#xff0c;第 i 条线段的左端点在 li&#xff0c;右端点在 ri​。小明用 m 个区间去框这些线段&#xff0c;第 i个区间的范围是 [Li​, Ri​]。如果一个线段有 至少一半 的长度被包含在某个区间内&#xff0c;…

linux ptrace 图文详解(二) PTRACE_TRACEME 跟踪程序

目录 一、基础介绍 二、PTRACE_TRACE 实现原理 三、代码实现 四、总结 &#xff08;代码&#xff1a;linux 6.3.1&#xff0c;架构&#xff1a;arm64&#xff09; One look is worth a thousand words. —— Tess Flanders 一、基础介绍 GDB&#xff08;GNU Debugger&…

记录致远OA服务器硬盘升级过程

前言 日常使用中OA系统突然卡死&#xff0c;刷新访问进不去系统&#xff0c;ping服务器地址正常&#xff0c;立马登录服务器检查&#xff0c;一看磁盘爆了。 我大脑直接萎缩了&#xff0c;谁家OA系统配400G的空间啊&#xff0c;过我手的服务器没有50也是30台&#xff0c;还是…

电网电压暂态扰动机理与工业设备抗失压防护策略研究

什么是晃电&#xff1f; 国标GB/T 30137-2013 中定义:工频电压方均根值突然降至额定值的90%~10%&#xff0c;持续时间为10ms~1min后恢复正常的现象。Acrel8757V 晃电的原因 1.系统侧因素 短路故障&#xff1a;雷击、线路接地、设备误碰等导致电网短路&#xff0c;故障点电压…