您当前的位置:首页 > 电脑百科 > 程序开发 > 算法

算法笔记:哈希表、映射和集合

时间:2022-03-29 11:41:51  来源:  作者:测试开发小记

hash函数是根据关键字key计算出应该存储地址的位置,哈希函数把key转成哈希值来定位数据存储的位置,是基于哈希函数建立的一种查找表,Python/ target=_blank class=infotextkey>Python 中的字典就是用哈希表来实现的。本文主要介绍哈希表、映射和集合这三种数据结构以及他们在python中的用法。

哈希表-Hash table

哈希表

哈希表(Hash table),也叫散列表,根据键(Key)访问在内存储存位置的数据,通过把键值映射到表中一个位置来访问记录,映射函数称为散列函数或者哈希函数,根据哈希函数建立的记录数据的表称为哈希表(散列表)。

比如键值为k,对应的值放在 f(k) 的存储位置上,这个对应关系 f 称为散列函数,通过它来建立的表称为散列表。

哈希碰撞

两个不同的key值得到相同的哈希值的情况称为哈希碰撞(Hash Collisions),也就是 f(k1) = f(k2)。哈希碰撞的解决方案有:开放寻址(Open Addressing)法、链地址法(ChAIning)、再哈希法(Rehash)和建立一个公共溢出区。

  • 开放寻址法:产生冲突后继续寻找下一个空闲的空间(没有被占用的存储地址),Python使用的就是这种方法。
  • 链地址法:散列到同一位置的元素,不继续往下寻找,而是将所有关键字为同义词的记录存储在同一线性链表中,HashMap就采用了链地址法。
  • 再散列函数法:产生冲突后,就再来一次哈希计算,直到没有冲突。
  • 建立一个公共溢出区:也就是建两个表,一个作为基本表,另一个是存储和基本表发生冲突元素的溢出表。

哈希冲突的发生,往往会降低字典和集合操作的速度。因此,为了保证其高效性,字典和集合内的哈希表,通常会保证其至少留有 1/3 的剩余空间。随着元素的不停插入,当剩余空间小于 1/3 时,Python 会重新获取更大的内存空间,扩充哈希表。

python 字典

Python 中的字典就是典型的哈希表,是一系列由键(key)和值(value)配对组成的元素的集合,其中value可以是任何数据类型,且可以重复。Key不能重复并且必须是不可变(immutable)的。

在 Python3.7+版本中,字典是有序的, 3.6 之前是无序的。

创建字典

# 创建空字典
>>> mydict={}
>>> type(mydict)
<class 'dict'>
>>> mydict
{}
>>> mydict = {1:"Apple",2:"banana"}

# dict()方法创建字典
>>> mydict = dict({1:"apple",2:"banana"})
>>> mydict = dict([(1, 'apple'), (2, 'banana')])
>>> mydict
{1: 'apple', 2: 'banana'}

# fromkeys()方法
>>> seq=(1,2,3)
>>> mydict = dict.fromkeys(seq)
>>> mydict
{1: None, 2: None, 3: None}
>>> mydict = dict.fromkeys(seq,'apple')
>>> mydict
{1: 'apple', 2: 'apple', 3: 'apple'}

理论上来说,直接使用{}创建字典比dict()方法效率更高, {} 会直接调用底层C代码。

直接使用Dict[Key] = ‘Value’的形式新增元素,可以增加任何数据类型,比如可以嵌套字典,列表等。如果key已经存在,则进行更新。

>>> mydict={}
>>> mydict[2] = 'banana'
>>> mydict
{2: 'banana'}

访问元素

直接使用key访问元素值,也可以使用 get(key) 方法获取,如果键不存在,调用 get() 函数可以返回一个默认值

>>> mydict = {1:"apple",2:"banana"}
>>> mydict[2]
'banana'
>>> mydict.get(2)
'banana'
>>> mydict.get(3,'null')
'null'

setdefault()方法也可以用来获取元素值,和get()方法不同的是,如果查找的key不存在,它会设置一个默认值(default=None):

>>> mydict.setdefault(2)
'banana'
>>> mydict.setdefault(3)
>>> mydict
{1: 'apple', 2: 'banana', 3: None}

>>> mydict.setdefault(4,'orange')
'orange'
>>> mydict
{1: 'apple', 2: 'banana', 3: None, 4: 'orange'}

删除元素

del删除元素

>>> mydict = {1:"apple",2:"banana"}
>>> del mydict[2]
>>> mydict
{1: 'apple'}

pop方法:

>>> mydict = {1:"apple",2:"banana"}
>>> mydict.pop(2)
'banana'
>>> mydict
{1: 'apple'}
>>>

popitem()用于随机删除任意键值对

清除字典元素

>>> mydict = {1:"apple",2:"banana"}
>>> mydict.clear()
>>> mydict
{}

合并字典

>>> mydict1 = {1:"apple",2:"banana"}
>>> mydict2 = {3:"orange"}
>>> mydict1.update(mydict2)
>>> mydict1
{1: 'apple', 2: 'banana', 3: 'orange'}
>>> 
# 或者
>>> {**mydict1,**mydict2}
{1: 'apple', 2: 'banana', 3: 'orange'}

获取字典key,value值

>>> mydict = {1:"apple",2:"banana"}
>>> mydict.keys()
dict_keys([1, 2])
>>> 
>>> mydict.values()
dict_values(['apple', 'banana'])

items()方法返回(key, value)对:

>>> mydict = {1:"apple",2:"banana"}
>>> mydict.items()
dict_items([(1, 'apple'), (2, 'banana')])
mydict = {1:"apple",2:"banana"}
for key,value in mydict.items():
	print(key)
	print(value)

python2中,has_key()可用于判断字典是否存在某个key:

>>> mydict = {1:"apple",2:"banana"}
>>> mydict.has_key(1)

python3删除了has_key()方法,可以使用 in 操作符来判断:

mydict = {1:"apple",2:"banana"}
if 1 in mydict:
	print(mydict(1))

# 或者
if 1 in mydict.keys():
	print(mydict(1))

字典排序

实际应用中,通常需要对字典进行排序,一般会根据键或值,进行升序或降序排序:

根据字典键升序排序

>>> mydict = {1:"apple",3:"banana",2:"orange"}
>>> sorted(mydict.items(), key=lambda x: x[0])
[(1, 'apple'), (2, 'orange'), (3, 'banana')]

根据字典值降序排序

>>> sorted(mydict.items(), key=lambda x: x[0], reverse=True)
[(3, 'banana'), (2, 'orange'), (1, 'apple')]
>>>

判断一个字典是否包含另一个字典

判断mydictA是否包含mydictB

>>> mydictA = {1:"apple",3:"banana",2:"orange"}
>>> mydictB = {1:"apple"}
>>> dict(mydictB, **mydictA) == mydictA
True

映射-Map

映射和哈希表类似,也是存储key-value对,通过键(Key)查找值(Value)。
JAVA 的HashMap() 和TreeMap()

  • map.set(key, value)
  • map.get(key)
  • map.has(key)
  • map.size()
  • map.clear()

python 映射函数

下面介绍一下python的map()函数用法:
map() 根据提供的函数对指定序列进行映射,返回映射函数返回值的新列表。一般结合lambda匿名函数一起使用:

>>> map(lambda x: x ** 2, [1, 2, 3, 4, 5])
[1, 4, 9, 16, 25]

两个list相加:

>>> list1 = [1, 2, 3]
>>> list2 = [4, 5, 6]  
>>> map(lambda x, y: x + y, list1, list2)
[5, 7, 9]

集合-Set

与列表(list)类似,但集合set没有重复元素,集合没有键和值的配对,是一系列无序的、唯一的元素组合。

字典和集合的内部结构都是一张哈希表,字典存储了哈希值(hash)、键和值这 3 个元素,而集合的哈希表内没有键和值的配对,只有单一的元素。和列表不一样,集合不支持索引操作。

java 的HashSet()和TreeSet()

  • set.add(value)
  • set.delete(value)
  • set.hash(value)

python集合

可以使用{ }创建集合:

>>> setA = {'apple', 'banana'}
# 或者 setA = set(["apple", "banana"])
>>> setB = {'apple', 'banana', 'orange'}

并集

>>> setA.union(setB)
set(['orange', 'apple', 'banana'])
>>> setA | setB
set(['orange', 'apple', 'banana'])

交集

>>> setA.intersection(setB)
set(['apple', 'banana'])
>>> setA & setB
set(['apple', 'banana'])
>>> 
>>> setB.intersection_update(setA)
>>> setB
{'banana', 'apple'}

isdisjoint() 方法可用于判断两个集合是否包含相同的元素,如果没有返回 True。

差集

>>> setB.difference(setA)
set(['orange'])
>>> setB-setA
set(['orange'])

子集判断

>>> setA = {'apple', 'banana'}
>>> setB = {'apple', 'banana', 'orange'}
>>> setA.issubset(setB)
True
>>> setA.issuperset(setB)
False
>>> setB.issuperset(setA)
True

对称差集

两个集合中不重复的元素集合

>>> setA.symmetric_difference(setB)
set(['orange'])
>>> setA ^ setB
set(['orange'])
>>> 
>>> setA.symmetric_difference_update(setB)
>>> setA
{'orange'}

增加元素

>>> setA.add("orange")
>>> setA
set(['orange', 'apple', 'banana'])

删除元素

remove()删除不存在的元素会报KeyError错误,可以使用discard()方法避免KeyError错误。

>>> setA = {'apple', 'banana', 'orange'}
>>> setA.remove('orange')
>>> setA
{'banana', 'apple'}
>>> 
>>> setA.remove('pear')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'pear
>>> setA.discard('pear')

pop() 方法也可以用来删除元素,用于删除最后一个元素,但是,集合是无序的,所以不知道到底删除的是哪一个元素。

>>> setA = {'apple', 'banana', 'orange'}
>>> setA.pop()
'banana'
>>> setA
{'orange', 'apple'}
>>>

清空集合

>>> setA = {'apple', 'banana'}
>>> setA.clear()
>>> setA
set()

冻结集合

冻结后集合不能添加或删除任何元素

>>> frozen_set = frozenset(['apple', 'banana'])
>>> frozen_set.add("orange")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'add'
>>>

集合排序

集合排序和列表、元组类似,使用 sorted(set) 方法排序:

>>> setA = {'apple', 'orange', 'banana'}
>>> sorted(setA)
['apple', 'banana', 'orange']

python集合运算

python集合支持以下运算:
1、in ,not in

>>> setA = {'apple', 'banana'}
>>> setB = {'apple', 'banana', 'orange'}
>>> 'apple' in setA
True
>>>

2、==,!=

>>> setA = {'apple', 'banana'}
>>> setB = {'apple', 'banana'}
>>> setA == setB
True
>>>

3、<=,<
setA <= setB:setA是setB的子集
setA < setB:setA是setB的真子集

>>> setA = {'apple', 'banana'}
>>> setB = {'apple', 'banana'}
>>> setA <= setB
True
>>> setA < setB
False
>>> setB = {'apple', 'banana', 'orange'}
>>> setA < setB
True

4、>=,>
setA >= setB:setA是setB的超集
setA > setB:setA是setB的真超集

>>> setA = {'apple', 'banana'}
>>> setB = {'apple', 'banana'}
>>> setA >= setB
True
>>> setA > setB
False
>>> setA = {'apple', 'banana', 'orange'}
>>> setA > setB
True

前面提到过,还支持:

  • |:并集
  • &:交集
  • -:差集
  • ^:对称差集

python集合特点

python集合有以下特点:
1、集合不按特定顺序保存元素,是无序的,不支持索引操作,集合本质上是一个哈希表,可以将集合转换为list后进行索引操作,也可以使用in 关键字。

setA = {'apple', 'banana'}
for fru in setA:
    print(fru, end="n")

输出

banana
apple

2、python集合只能添加不可变(immutable)的实例,比如可以添加元组(tuple),字符串(string),不能添加列表(list),如果添加的元素为list,可以使用update方法,update方法用于新增多个元素。

>>> a=(1,2)
>>> setA.add(a)
>>> setA
{(1, 2), 'bapple', 'anana'}
>>> 
>>> b=[1,2]
>>> setA.add(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> setA.update(b)
>>> setA
{(1, 2), 1, 2, 'bapple', 'anana'}
>>>

复杂度分析

相比于数组,列表和元组,哈希表和集合的性能更优,特别是对于查找、添加和删除操作,字典都能在常数时间复杂度内完成。对于查找,数组的时间复杂度为 O(n),如果使用二分查找,也需要 O(logn) 的时间复杂度,但需要对数组进行排序,至少需要O(nlogn) 的时间复杂度。

算法笔记:哈希表、映射和集合

http://www.bigocheatsheet.com/

--THE END--



Tags:算法   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
诱导付费、自动扣费……微短剧被质疑借助算法精准“围猎”老年人
诱导付费、自动扣费、重复收费&hellip;&hellip;聚焦身边的消费烦心事⑦丨一些微短剧被质疑借助算法精准“围猎”老年人中工网北京3月31日电(工人日报&mdash;中工网记者刘兵)...【详细内容】
2024-04-01  Search: 算法  点击:(11)  评论:(0)  加入收藏
分析网站SEO快速排名算法对网站具体的影响效果
亲爱的朋友们,今天我想和大家分享一个我们都关心的话题&mdash;&mdash;网站SEO快速排名算法对网站我们身处一个信息爆炸的时代,如何在海量的信息中脱颖而出,成为了一个我们不得...【详细内容】
2024-03-28  Search: 算法  点击:(20)  评论:(0)  加入收藏
当prompt策略遇上分治算法,南加大、微软让大模型炼成「火眼金睛」
近年来,大语言模型(LLMs)由于其通用的问题处理能力而引起了大量的关注。现有研究表明,适当的提示设计(prompt enginerring),例如思维链(Chain-of-Thoughts),可以解锁 LLM 在不同领域的...【详细内容】
2024-03-12  Search: 算法  点击:(20)  评论:(0)  加入收藏
谷歌宣布更新搜索算法:打击AI生成内容,提高搜索结果质量
IT之家 3 月 6 日消息,谷歌于当地时间 5 日发文宣布,针对用户对搜索结果质量下降的反馈,将对算法进行调整,旨在打击 AI 生成的内容以及内容农场等垃圾信息,使用户能够看到更多“...【详细内容】
2024-03-06  Search: 算法  点击:(41)  评论:(0)  加入收藏
小红书、视频号、抖音流量算法解析,干货满满,值得一看!
咱们中国现在可不是一般的牛!网上的网友已经破了十个亿啦!到了这个互联网的新时代,谁有更多的人流量,谁就能赢得更多的掌声哦~抖音、小红书、、视频号,是很多品牌必争的流量洼地...【详细内容】
2024-02-23  Search: 算法  点击:(17)  评论:(0)  加入收藏
雪花算法详解与Java实现:分布式唯一ID生成原理
SnowFlake 算法,是 Twitter 开源的分布式 ID 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 ID。在分布式系统中的应用十分广泛,且 ID 引入了时间戳...【详细内容】
2024-02-03  Search: 算法  点击:(54)  评论:(0)  加入收藏
简易百科之什么是搜索引擎的PageRank算法?
简易百科之什么是搜索引擎的PageRank算法?在互联网时代,搜索引擎是我们获取信息的重要工具。而PageRank算法则是搜索引擎的核心技术之一,它决定了网页在搜索结果中的排名。那么...【详细内容】
2024-01-24  Search: 算法  点击:(57)  评论:(0)  加入收藏
PageRank算法揭秘:搜索引擎背后的魔法师的工作原理
PageRank(PR)算法是由谷歌创始人之一的拉里&middot;佩奇LarryPage命名的一种衡量网站页面重要性的方法。根据谷歌的说法,PageRank通过计算页面链接的数量和质量来粗略估计分...【详细内容】
2024-01-23  Search: 算法  点击:(45)  评论:(0)  加入收藏
程序开发中常用的十种算法,你用过几种?
当编写程序时,了解和使用不同的算法对解决问题至关重要。以下是C#中常用的10种算法,每个算法都伴随着示例代码和详细说明。1. 冒泡排序 (Bubble Sort):冒泡排序是一种简单的比...【详细内容】
2024-01-17  Search: 算法  点击:(46)  评论:(0)  加入收藏
百度最新的搜索引擎算法是什么样的?
百度搜索引擎算法是百度用来决定网页排名的算法。它是百度搜索技术的核心,也是百度作为全球最大的中文搜索引擎的基石。随着互联网的发展和用户需求的不断变化,百度搜索引擎算...【详细内容】
2024-01-10  Search: 算法  点击:(91)  评论:(0)  加入收藏
▌简易百科推荐
小红书、视频号、抖音流量算法解析,干货满满,值得一看!
咱们中国现在可不是一般的牛!网上的网友已经破了十个亿啦!到了这个互联网的新时代,谁有更多的人流量,谁就能赢得更多的掌声哦~抖音、小红书、、视频号,是很多品牌必争的流量洼地...【详细内容】
2024-02-23  二手车小胖说    Tags:流量算法   点击:(17)  评论:(0)  加入收藏
雪花算法详解与Java实现:分布式唯一ID生成原理
SnowFlake 算法,是 Twitter 开源的分布式 ID 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 ID。在分布式系统中的应用十分广泛,且 ID 引入了时间戳...【详细内容】
2024-02-03   一安未来  微信公众号  Tags:雪花算法   点击:(54)  评论:(0)  加入收藏
程序开发中常用的十种算法,你用过几种?
当编写程序时,了解和使用不同的算法对解决问题至关重要。以下是C#中常用的10种算法,每个算法都伴随着示例代码和详细说明。1. 冒泡排序 (Bubble Sort):冒泡排序是一种简单的比...【详细内容】
2024-01-17  架构师老卢  今日头条  Tags:算法   点击:(46)  评论:(0)  加入收藏
百度推荐排序技术的思考与实践
本文将分享百度在推荐排序方面的思考与实践。在整个工业界的推广搜场景上,特征设计通常都是采用离散化的设计,需要保证两方面的效果,一方面是记忆,另一方面是泛化。特征都是通过...【详细内容】
2024-01-09  DataFunTalk  微信公众号  Tags:百度推荐   点击:(80)  评论:(0)  加入收藏
什么是布隆过滤器?如何实现布隆过滤器?
以下我们介绍了什么是布隆过滤器?它的使用场景和执行流程,以及在 Redis 中它的使用,那么问题来了,在日常开发中,也就是在 Java 开发中,我们又将如何操作布隆过滤器呢?布隆过滤器(Blo...【详细内容】
2024-01-05  Java中文社群  微信公众号  Tags:布隆过滤器   点击:(91)  评论:(0)  加入收藏
面向推荐系统的深度强化学习算法研究与应用
随着互联网的快速发展,推荐系统在各个领域中扮演着重要的角色。传统的推荐算法在面对大规模、复杂的数据时存在一定的局限性。为了解决这一问题,深度强化学习算法应运而生。本...【详细内容】
2024-01-04  数码小风向    Tags:算法   点击:(104)  评论:(0)  加入收藏
非负矩阵分解算法:从非负数据中提取主题、特征等信息
非负矩阵分解算法(Non-negativeMatrixFactorization,简称NMF)是一种常用的数据分析和特征提取方法,主要用于从非负数据中提取主题、特征等有意义的信息。本文将介绍非负矩阵分解...【详细内容】
2024-01-02  毛晓峰    Tags:算法   点击:(73)  评论:(0)  加入收藏
再谈前端算法,你这回明白了吗?
楔子 -- 青蛙跳台阶一只青蛙一次可以跳上一级台阶,也可以跳上二级台阶,求该青蛙跳上一个n级的台阶总共需要多少种跳法。分析: 当n=1的时候,①只需要跳一次即可;只有一种跳法,即f(...【详细内容】
2023-12-28  前端爱好者  微信公众号  Tags:前端算法   点击:(113)  评论:(0)  加入收藏
三分钟学习二分查找
二分查找是一种在有序数组中查找元素的算法,通过不断将搜索区域分成两半来实现。你可能在日常生活中已经不知不觉地使用了大脑里的二分查找。最常见的例子是在字典中查找一个...【详细内容】
2023-12-22  小技术君  微信公众号  Tags:二分查找   点击:(79)  评论:(0)  加入收藏
强化学习算法在资源调度与优化中的应用
随着云计算和大数据技术的快速发展,资源调度与优化成为了现代计算系统中的重要问题。传统的资源调度算法往往基于静态规则或启发式方法,无法适应动态变化的环境和复杂的任务需...【详细内容】
2023-12-14  职场小达人欢晓    Tags:算法   点击:(169)  评论:(0)  加入收藏
站内最新
站内热门
站内头条