数据结构 | 搜索和排序——搜索

目录

一、顺序搜索

二、分析顺序搜索算法

三、二分搜索

四、分析二分搜索算法

五、散列

5.1 散列函数

5.2 处理冲突

5.3 实现映射抽象数据类型


搜索是指从元素集合中找到某个特定元素的算法过程。搜索过程通常返回True或False,分别表示元素是否存在。有时,可以修改搜索过程,使其返回目标元素的位置。

Python提供了运算符in,通过它可以方便地检查元素是否在列表中。

>>> 15 in [3,5,2,4,1]
False
>>> 3 in [3,5,2,4,1]
True

一、顺序搜索

存储在列表等集合中的数据项彼此存在线性或顺序关系,每个数据项的位置与其他数据项相关。在Python列表中,数据项的位置就是它的下标。因为下标是有序的,所以能够顺序访问,由此可以进行顺序搜索

顺序搜索的原理:从列表中的第一个元素开始,沿着默认的顺序逐个查看,直到找到目标元素或者查完列表。如果查完列表后仍没有找到目标元素,则说明目标元素不在列表中。

无序列表的顺序搜索:

def sequentialSearch(alist,item):pos=0found=Falsewhile pos<len(alist) and not found:if alist[pos]==item:found=Trueelse:pos=pos+1return found

二、分析顺序搜索算法

在无序列表中进行顺序搜索时的比较次数
最好情况最坏情况普通情况
存在目标元素1nn/2
不存在目标元素nnn

有序列表的顺序搜索:

def orderedSequentialSearch(alist,item):pos=0found=Falsestop=Falsewhile pos<len(alist) and not found and not stop:if alist[pos]==item:found=Trueelse:if alist[pos]>item:stop=Trueelse:pos=pos+1return found

只有当列表不存在目标元素时,有序排列元素才会提高顺序搜索的效率。

在有序列表中进行顺序搜索时的比较次数
最好情况最坏情况普通情况
存在目标元素1nn/2
不存在目标元素1nn/2

三、二分搜索

在顺序搜索时,如果第一个元素不是目标元素,最多还要比较n-1次。但二分搜索不是从第一个元素开始搜索,而是从中间的元素着手。如果这个元素是目标元素,那就立即停止搜索;如果不是,则可以利用列表有序的特性,排除一半的元素。如果目标元素比中间的元素大,就可以直接排除列表左半部分和中间的元素。这是因为,如果列表包含目标元素,它必定位于右半部分。

有序列表的二分搜索:

def binarySearch(alist,item):first=0last=len(alist)-1found=Falsewhile first<=last and not found:midpoint=(first+last)//2if alist[midpoint]==item:found=Trueelse:if item<alist[midpoint]:last=midpoint-1else:first=midpoint+1return found

这个算法是分治策略的好例子。分治是指将问题分解成小问题,以某种方式解决小问题,然后整合结果,以解决最初的问题。对列表进行二分搜索时,先查看中间的元素。如果目标元素小于中间的元素,就只需要对列表的左半部分进行二分搜索。同理,如果目标元素更大,则只需对右半部分进行二分搜索。两种情况下,都是针对一个更小的列表递归调用二分搜索函数。

二分搜索的递归版本:

def binarySearch(alist,item):if len(alist)==0:return Falseelse:midpoint=len(alist)//2if alist[midpoint]==item:return Trueelse:if item<alist[midpoint]:return binarySearch(alist[:midpoint],item)else:return binarySearch(alist[midpoint+1:],item)

四、分析二分搜索算法

二分搜索算法的时间复杂度是O(logn)。

尽管二分搜索通常优于顺序搜索,但当n较小时,排序引起的额外开销可能并不划算。实际上应该始终考虑,为了提高搜索效率,额外排序是否值得。如果排序一次后能够搜索多次,那么排序的开销不值一提。然而,对于大型列表而言,只排序一次也会有昂贵的计算成本,因此从头进行顺序排序可能是更好的选择。

五、散列

通过散列可以构建一个时间复杂度为O(1)的数据结构。

散列表是元素集合,其中的元素以一种便于查找的方式存储。散列表中的每个位置通常被称为,其中可以存储一个元素。槽用一个从0开始的整数标记,例如0号槽、1号槽、2号槽,等等。初始情形下,散列表中没有元素,每个槽都是空的。可以用列表来实现散列表,并将每个元素初始化为Python中的特殊值None。

散列函数将散列表中的元素与其所属位置对应起来。对散列表中的任一元素,散列函数返回一个介于0和m-1之间的整数。首先来看第一个散列函数,它有时被称作“取余函数”,即用一个元素除以表的大小,并将得到的余数作为散列值。取余函数是一个很常见的散列函数,这是因为结果必须在槽编号范围内。

计算出散列值后,就可以将每个元素插入到相应的位置。在11个槽中,有六个被占用。占用率被称为载荷因子,记作\lambda,定义如下:\lambda=元素个数/散列表大小。

搜索目标元素时,仅需使用散列函数计算出该元素的槽编号,并查看对应的槽中是否有值。因为计算散列值并找到相应位置所需的时间是固定的,所以搜索操作的时间复杂是O(1)。

如果两个元素的散列值相同,散列函数会将两个元素都放入同一个槽,这种情况叫作冲突,也叫做“碰撞”。显然,冲突给散列函数带来了问题。

5.1 散列函数

给定一个元素集合,能将每个怨怒是映射到不同的槽,这种散列函数称作完美散列函数

我们的目标是创建这样一个散列函数:冲突数最少,计算方便,元素均匀分布于散列表中。有多种常见的方法来扩展取余函数,下面介绍其中的几种。

折叠法先将元素切成等长的部分(最后一部分的长度可能不同),然后将这些部分相加,得到散列值。假设元素是电话号码436-555-4601,以2位为一组进行切分,得到43、65、55、46和01,将这些数字相加后,得到210.假设散列表有11个槽,接着需要用210除以1,并保留余数1,所以,电话号码436-555-4601被映射到散列表的1号槽。有些折叠法更进一步,在加总前每隔一个数反转一次。就本例而言,反转后的结果是:43+56+55+64+01=219,219%11=10。

另一个构建散列函数的数学技巧是平方取中法:先将元素取平方,然后提取中间几位数。如果元素是44,先计算44*44=1936,然后提取中间两位93,继续进行取余的步骤,得到5(93%11)。

我们也可以为基于字符的元素(比如字符串)创建散列函数。可以将单词"cat"看作序数值序列,如下所示:

>>> ord('c')
99
>>> ord('a')
97
>>> ord('t')
116

因此,可以将这些序数值相加,并采用取余法得到散列值。

def hash(astring,tablesize):sum=0for pos in range(len(astring)):sum=sum+ord(astring[pos])return sum%tablesize

有趣的是,针对异序词,这个散列函数总是得到相同的散列值。要弥补这一点,可以同字符位置作为权重因子。

def hash(astring,tablesize):sum=0for pos in range(len(astring)):sum=sum+ord(astring[pos])*(pos+1)return sum%tablesize

5.2 处理冲突

当两个元素被分到同一个槽中时,必须通过一种系统化方法在散列表中安置第二个元素。这个过程被称为处理冲突

一种方法是在散列表中找到另一个空槽,用于放置引起冲突的元素。简单的做法是从起初的散列值开始,顺序遍历散列表,知道找到一个空槽。注意,为了遍历散列表,可能需要往回检查第一个槽。这个过程被称为开放定址法,它尝试在散列表中寻找下一个空槽或地址。由于是逐个访问槽,因此这个做法被称作线性探测

线性探测有个缺点,那就是会使散列表中的元素出现聚集现象。也就是说,如果一个槽发生太多冲突,线性探测会填满附近的槽,而这会影响后续插的元素。要避免元素聚集,一种方法是扩展线性探测,不再依次顺序查找空槽,而是跳过一些槽,这样做能使引起冲突的元素分布得更均匀。采用“加3”探测策略处理冲突后的元素是指发生冲突时,为了找到空槽,该策略每次跳两个槽。

再散列泛指在发生冲突后寻找另一个槽的过程。采用线性探测时,再散列函数是newhashvalue=rehash(oldhanshvalue),并且rehash(pos)=(pos+1)%sizeoftable。“加3”探测策略的再散列函数可以定义为rehash(pos)=(pos+3)%sizeoftable。也就是说,可以将再散列函数定义为rehash(pos)=(pos+skip)%sizeoftable。注意,“跨步(skip)的大小要能够保证表中所有的槽最终都被访问到,否则就会浪费槽资源。要保证这一点,常常建议散列表的大小为素数

平方探测是线性探测的一个变体,它不采用固定的跨步,而是通过再散列函数递增散列值。如果第一个散列值是h,后续的散列值就是h+1、h+4、h+9、h+16,等等。换句话说,平方探测的跨步是一系列完全平方数。

另一种处理冲突的方法就是让每个槽有一个指向元素集合(或链表)的引用。链接法允许散列表中的同一个位置上存在多个元素。发生冲突时,元素仍然被插入其散列值对应的槽中。不过,随着同一个位置上的元素越来越多,搜索变得越来越困难。

5.3 实现映射抽象数据类型

字典是最有用的Python集合之一。字典是存储键-值对的数据类型。键用来查找关联的值,这个概念常常被称作映射

映射抽象数据类型定义如下。它是将键和值关联起来的无序集合,其中的键是不重复的,键和值之间是一一对应的关系。映射支持以下操作。

  • Map()创建一个空的映射,它返回一个空的映射集合。
  • put(key,val)往映射中加入一个新的键值对。如果键已经存在,就用新值替换旧值。
  • get(key)返回key对应的值。如果key不存在,则返回None。
  • del通过del map[key]这样的语句从映射中删除键-值对。
  • len()返回映射中存储的键-值对的数目。
  • in通过key in map这样的语句,在键存在时返回True,否则返回False。
class HashTable:def __init__(self):self.size=11self.slots=[None]*self.sizeself.data=[None]*self.sizedef put(self,key,data):hashvalue=self.hashfunction(key,len(self.slots))if self.slots[hashvalue]==None:self.slots[hashvalue]=keyself.data[hashvalue]=dataelse:if self.slots[hashvalue]==key:self.data[hashvalue]=data  #替换else:nextslot=self.rehash(hashvalue,len(self.slots))while self.slots[nextslot]!=None and self.slots[nextslot]!=key:nextslot=self.rehash(nextslot,len(self.slots))if self.slots[nextslot]==None:self.slots[nextslot]=keyself.data[nextslot]=dataelse:self.data[nextslot]=data  #替换def hashfunction(self,key,size):return key%sizedef rehash(self,oldhash,size):return (oldhash+1)%sizedef get(self,key):startslot=self.hashfunction(key,len(self.slots))data=Nonestop=Falsefound=Falseposition=startslotwhile self.slots[position]!=None and not found and not stop:if self.slots[position]==key:found=Truedata=self.data[position]else:position=self.rehash(position,len(self.slots))if position==startslot:stop=Truereturn datadef __getitem__(self, key):return self.get(key)def __setitem__(self, key, data):self.put(key,data)if __name__=="__main__":H=HashTable()H[54]="cat"H[26]="dog"H[93]="lion"H[17]="tiger"H[77]="bird"H[31]="cow"H[44]="goat"H[55]="pig"H[20]="chicken"print(H.slots)print(H.data)print(H[20])print(H[17])H[20]="duck"print(H[20])print(H.data)print(H[99])

HashTable类的最后两个方法提供了额外的字典功能。我们重载__getitem__和__settiem__ ,以通过[ ]进行访问。这意味着创建HashTable类之后,就可以使用熟悉的索引运算符了。

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

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

相关文章

gradle项目Connection timed out,build时先下载gradle问题download gradle-x.x-bin.zip

IDEA 导入 Gradle 项目&#xff0c;编译的时候会默认下载 配置版本的Gradle.zip问题&#xff0c;一般会下载失败&#xff0c;提示Connection timed out&#xff0c;连接超时。 解决办法&#xff1a; 修改项目根目录下gradle目录下的gradle-wrapper.properties文件&#xff0c;…

Kafka3.0.0版本——生产者如何提高吞吐量

目录 一、生产者提高吞吐量参数设置二、产者提高吞吐量代码示例 一、生产者提高吞吐量参数设置 batch.size&#xff1a;设置批次大小&#xff0c;默认16klinger.ms&#xff1a;设置等待时间&#xff0c;修改为5-100msbuffer.memory&#xff1a;设置缓冲区大小&#xff0c; 默认…

JGJ80-2016建筑施工高处作业安全技术规范

为规范建筑施工高处作业及其管理&#xff0c;做到防护安全、技术先进、经济合理&#xff0c;制定本规范。 本规范适用于建筑工程施工高处作业中的临边、洞口攀登、悬空、操作平台、交叉作业及安全网搭设等项作业。 本规范亦适用于其他高处作业的各类洞、坑、沟、槽等部位的施…

高性能计算集群使用

一、PuTTY的下载与安装 PuTTY是一款开源的连接软件&#xff0c;是 SSH、Telnet、Rlogin 和 SUPDUP 网络协议的客户端程序。 下载网址&#xff1a;Download PuTTY - a free SSH and telnet client for Windows 安装好后连接自己的服务器 输入用户名和密码&#xff0c;回车登录…

一些不错的VSCode设置和插件

设置 同步设置 我们做的各项设置&#xff0c;不希望再到其他机器的时候还得再重新配置一次。VSCode中我们可以登陆微软账号或者GitHub账号&#xff0c;登陆后我们可以开启同步设置。开启设置同步&#xff0c;根据提示登陆即可。 允许侧边栏水平滑动 在目录层次较深或者文件…

Docker-Compose编排与部署

目录 Docker Compose Compose的优点 编排和部署 Compose原理 Compose应用案例 安装docker-ce 阿里云镜像加速器 安装docker-compose docker-compose用法 Yaml简介 验证LNMP环境 Docker Compose Docker Compose 的前身是 Fig&#xff0c;它是一个定义及运行多个 Dock…

langchain-ChatGLM源码阅读:参数设置

文章目录 上下文关联对话轮数向量匹配 top k控制生成质量的参数参数设置心得 上下文关联 上下文关联相关参数&#xff1a; 知识相关度阈值score_threshold内容条数k是否启用上下文关联chunk_conent上下文最大长度chunk_size 其主要作用是在所在文档中扩展与当前query相似度较高…

【Spring Boot】(二)Spring Boot 配置文件的探索之旅

文章目录 前言一、配置文件的作用二、配置文件的格式2.1 Spring Boot 配置文件格式2.2 properties 和 yml 的区别 三、properties 配置文件3.1 properties 基本语法3.2 配置文件的读取3.3 properties 优缺点分析 四、yml 配置文件说明4.1 yml 基本语法4.2 yml 使用案例4.3 yml …

Android Ble蓝牙App(三)特性和属性

Ble蓝牙App&#xff08;三&#xff09;特性使用 前言正文一、获取属性列表二、属性适配器三、获取特性名称四、特性适配器五、加载特性六、显示特性和属性七、源码 前言 在上一篇中我们完成了连接和发现服务两个动作&#xff0c;那么再发现服务之后要做什么呢&#xff1f;发现服…

在centos7上使用非编译方式安装ffmpeg

很多在centos7上安装ffmpeg的教程都需要使用编译方式的安装&#xff1b;编译时间较长而且需要配置; 后来搜索到可以通过加载rpm 源的方式实现快速便捷操作 第一种方式&#xff1a; 首先需要安装yum源&#xff1a; yum install epel-release yum install -y https://mirrors.…

GPU版PyTorch对应安装教程

一、正确安装符合自己电脑的对应GPU版本的PyTorch之前需要了解三个基本概念 算力、CUDA driver version、CUDA runtime version ①算力&#xff1a;需要先知道你的显卡&#xff0c;之后根据官网表格进行对应&#xff0c;得到算力 ②CUDA driver version&#xff1a;电脑上显卡…

python编写小程序有界面,python编写小程序的运行

大家好&#xff0c;小编为大家解答python编写小程序怎么看代码的的问题。很多人还不知道python编写小程序的运行&#xff0c;现在让我们一起来看看吧&#xff01; Python第一个简单的小游戏 temp input("请猜一猜姐姐的幸运数字是&#xff1a; ") guess int(temp) …

99%的人做效果图都会忽略的问题!为什么你的效果图没有亚洲面孔?

不知道各位设计师有没有发现一个问题&#xff0c;我们做了不少效果图&#xff0c;也积攒了很多素材&#xff0c;但是出现在我们效果图的人物几乎都是外国人&#xff01; 可能你会说是亚洲人的素材实在太少&#xff0c;但本质是对“人”不够重视&#xff0c;觉得随便“复制粘贴”…

数据结构 | 利用二叉堆实现优先级队列

目录 一、二叉堆的操作 二、二叉堆的实现 2.1 结构属性 2.2 堆的有序性 2.3 堆操作 队列有一个重要的变体&#xff0c;叫作优先级队列。和队列一样&#xff0c;优先级队列从头部移除元素&#xff0c;不过元素的逻辑顺序是由优先级决定的。优先级最高的元素在最前&#xff…

火爆全网,Python自动化测试Allure测试报告生成,最强总结...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Allure测试报告框…

JavaWeb(9)——前端综合案例3(悬停显示下拉列表)

一、实例需求 ⌛ 实现类似百度首页的“一个简单的鼠标悬停显示的下拉列表效果”。 二、代码实现 ☕ <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>.dropdown-cont…

外部链接跳转到vue项目传递参数实现单点登录

1、问题背景描述&#xff1a; 我有一个困扰了很久项目需求&#xff0c;前台门户用的MVC&#xff0c;前台登录之后需要能点击某个按钮就能进入后台vue开发的前端项目&#xff0c;不需要重新登录。这个需求中mvc项目相对于vue项目来说是外部链接&#xff0c;他要跳转到vue项目&a…

9、Kubernetes核心技术 - Volume

目录 一、概述 二、卷的类型 三、emptyDir 四、hostPath 五、NFS 5.1、master服务器上搭建nfs服务器 5.2、各个slave节点上安装nfs客户端 5.3、创建Pod 六、PV和PVC 6.1、PV 6.1.1、PV资源清单文件示例 6.1.2、PV属性说明 6.1.3、PV的状态 6.2、PVC 6.2.1、PVC资…

git开发过程中的使用

1、先创建本地分支&#xff0c;然后修改代码 2、本地提交 push 3、合并为主分支 回到master分支

Bigemap如何添加谷歌地图?

工具 Bigemap gis office地图软件 BIGEMAP GIS Office-全能版 Bigemap APP_卫星地图APP_高清卫星地图APP 打开软件&#xff0c;要提示需要授权和添加地图&#xff0c;然后去点击选择地图这个按钮&#xff0c;列表中有个添加按钮点进去选择添加地图的方式。 第一种方式&#x…