第八章 查找【数据结构】1

简介: 第八章 查找【数据结构】1

配套资源下载

数据结构资源下载导航【数据结构】

第8章 查找

可以参考改网站

https://visualgo.net/zh

8.1 概述

所谓查找,就是根据给定的某个值,在一组记录集合中确定某个特定的数据元素(记录)或 者找到属性值符合特定条件的某些记录。
其中,由同一类型的数据元素(或记录)构成的集合称为“查找表”,也就是查找对象的集合。 关键字是查找表中“特定的"数据元素(或记录)的某个数据项的值,用来识别一个数据元素(或 记录)。若关键字可以唯一地识别一个记录,则称其为“主关键字”;若关键字能识别若干个记录,则称其为“次关键字”

通常,对查找表进行的操作有以下几种。

①查询某个特定的数据元素是否在查找表中

②检索某个特定的数据元素的各种属性,

③在查找表中插入某个数据元素

④从查找表中删除某个数据元素

一般前两项称为“静态查找”,后两项称为“动态查找”

如果在查找表中存在待查记录,则查找成功,并输出该记录的相关信息,或指示该记录在查找表中的位置;否则查找不成功,给出空记录或空指针。


平均查找长度:为确定某元素在查找表中的位置,需要和给定值进行比较的关键字个数的期望值,称为该查找算法查找成功时的“平均查找长度”(average search length,ASL)。

对于长度为n的查找表,查找成功时的平均查找长度为ASL= ∑ni=1PiCi。其中Pi为查找第i个记录的概率, 且 ∑ni=1p=1。Ci为找到该记录时,曾经和给定值比较过的关键字的个数。

8.2 基于线性表的查找

8.2.1顺序查找

顺序查找表的数据类型描述如下。

#define MAXSIZE 1000    //顺序查找表记录数目
typedef int KeyType;    //假设关键字类型为整型
typedef int OtherType; 
typedef struct{
  KeyType key;    //关键字项
  //其他数据项,类型OtherType依赖于具体应用而定义
  OtherType other_data;
}RecordType;//记录类型
typedef struct{
  RecordType r [MAXSIZE+1]; 
  int length;   //序列长度,即实际记录个数
}SeqRList;      //记录序列类型,即顺序表类型

为符合C语言标准,记录序列的下标都从0开始,但下标为0的位置一般作为“监视哨"或

空闲不用,实际的记录序列都从下标为1的记录开始,


例如,一个顺序查找表的记录序列的关键字分别为21,37,88,19,92,5,64,56,80,72,13,待查找记录的关键字为64。

由于整个记录序列是无序的,所以查找时只能从前向后或从后向前进行,从概率的角度来说选择哪种进行都是可以的。


算法8-1为从后向前进行顺序查找的实现。

相关代码请看配套资源

1-顺序查找.c

int main(){
  SeqRList L;
  input(&L);
  int s=SeqSearch(L,64);
  printf("s:%d\n",s);
  int s2=SeqSearch2(L,64); 
  printf("s2:%d\n",s2);
//  output(&L);
  return 0;
}

显然,该算法的时间代价主要消耗在while循环处,即两个循环判断条件的执行。其中,第

个循环条件i>=1是保证循环的结束,即边界条件的判定。事实上,在保证查找成功的情况下,该条件的执行只是白白浪费时间。有实验表明,在查找记录超过1000条时,该条语句的执行时间占到了整个算法执行时间的60%。


算法8-2为改进后(即加“监视哨")的顺序查找实现。

相关代码请看配套资源

1-顺序查找.c

int main(){
  SeqRList L;
  input(&L);
  int s=SeqSearch(L,64);
  printf("s:%d\n",s);
  int s2=SeqSearch2(L,64); 
  printf("s2:%d\n",s2);
//  output(&L);
  return 0;
}


可以看出,算法利用了0号位管作为“监视哨”,人为地将其关键字设置为待查记录的键字K。这样从后向前进行查找时,省去了边界条件的判断,无论查找成功或失败都能找到

该条记录在查找表中的位置。如果是i>0的位置,则在找成功;如果是i=0的位置,则查找失败。


对顺序表,其平均查找长度为ASL=nP1+(n-1)P2+2Pn-1+Pn

在等概率查找的情况下,Pi=1/n,Ci=n-i+1

顺序表查找成功时的平均查找长度为ASLSUCC-=1/n ∑ni=1(n-i+1)= (n+1)/2

也就是说,香找成功时的平均比较次数是表长的一半,那么当表长很大时,查找的效率比较低。但是,顺序查找的优势在于对表的特性没有要求,数据元素可以任意排列,插入元素可以直接加到表尾。


如果查找不成功,则需要讲行n+1次比较才能确定查找失败。假设被查找的关键字记录在线性表中(即查找成功)的概率为p,不在线性表中(即查找失败)的概率为1-p。那么,成功和失败的查找都考虑在内时的平均比较次数为

ASL=p(n+1)/2+(1-p)(n+1)=(n+1)(1-p/2)

可以看出,(n+1)/2<ASL<(n+1)


通常情况下,查找概率无法事先测定,为了加快查找速度,可以建立自组织线性表。自组织 线性表是根据实际的记录访问模式在线性表中修改记录顺序,主要使用启发式规则决定如何重 新排列线性表。一般管理自组织线性表的启发式规则有以下三种。

①最不频繁使用法(least frenquently used,LFU),也叫做“计数法”。。

②最近最少使用法(least recently used,LRU),也叫做“移至前端法”。

③转置(transpose)方法,也叫做“调换方法”。

8.2.2 折半查找

【算法 8-3】折半查找的非递归实现

相关代码请看配套资源

2-折半查找.c

int main(){
  SeqRList L;
  input(&L);
  printf("查找的索引:\n");
  int s=BinSrch(L,3);
  printf("s:%d\n",s);
  output(&L);
  return 0;
}

运行结果如下:


查找成功时,关键字的比较次数最多不超过⌊log2n⌋+1

查找失败时,关键字的比较次数最多不超过⌊log2n⌋+1

折半查找成功时的平均查找长度
ASL=∑ni=1PiCi=1/n ∑ni=1Ci=1/n ∑hj=1 x2j-1=(n+1)/n log2(n+1)-1

当n>50时,可得近似结果ASL≈ log2(n+1)-1

折半查找适用于一些建立就很少改动,而且与需要经常查找的有序查找表

8.2.3 索引查找

8.3 基于树的查找

8.3.1 二叉排序树

二叉排序树(binary search tree,BST),又称为“二叉查找树",是一种高效的数据结构。二叉排序树或者是一棵空树,或者是具有如下特性的二叉树。
①若它的左子树不空,则左子树上所有结点的值均小于根结点的值

②若它的右子树不空,则右子树上所有结点的值均大于根结点的值

③它的左、右子树也都分别是二叉排序树

显然,这是一个递归定义,首先要保证结点的值之间具有可比性,另外,关键字之间不允许重复出现。在实际应用中,不能保证被查找记录的关键字互不相同,可将 ①中的“小于"改为“小于等于”,或将②中的“大于"改为“大于等于”,甚至可同时修改这两个性质。

中序遍历二叉排序树便得到递增的有序序列。

二叉排序树可以使用二叉链表作为存储结构,数据类型描述如下。

typedef int KeyType;  //假设的关键字类型
typedef int OtherType;
typedef struct Node{
  KeyType key;
  OtherType other_data;
  struct Node* Lchild,* Rchild;  
}BSTNode,* BSTree;

相关代码请看配套资源

3-二叉排序树.c

代码运行结果如下


1.二叉排序树的查找

由于二叉排序树可以看成是一个有序表,所以在二叉排序树上进行查找类似于折半查找,即逐步缩小查找范围的过程。具体如下 。

①若给定值等于根结点的关键字,则查找成功。
②若给定值小于根结点的关键字,则继续在左子树上进行查找。

③若给定值大于根结点的关键字,则继续在右子树上进行查找。

由图8-9可以看出,整个查找过程类似于在折半查找的判定树上进行折半查找。从根结点出发,沿着左子树或右子树逐层向下直至关键字等于给定值的结点——查找成功;从根结点出发,沿着左子树或右子树逐层向下直至指针指向空结点(方块处)为止——查找失败。


基于二叉排序树查找的非递归实现见算法8-4.

相关代码请看配套资源

代码运行结果如下


显然,根据递归特性,算法也可以用递归实现,见算法8-5。

相关代码请看配套资源

代码运行结果如下


2.二叉排序树的插入

首先查找待插入的记录是否在树中,如果存在则不允许插入重复关键字;如果直到找到叶子结点仍没有发现重复关键字,则把待插结点作为新的叶子结占插入。具体地,若二叉排序树为空树,则新插入的结点为新的根点:否则,新插入的结点必为一个新的叶子结点,其插入位置由查找不成功的位置确定。


[算法8-6]二叉排序树的插入

相关代码请看配套资源

代码运行结果如下


3.二叉排序树的建立

二叉排序树的建立是基于插人算法进行的,根据给定的关键字顺序依次插入得到。

可以看出,二叉排序树的形态完全由输入顺序决定,相同的关键字不同的输人顺序会产生不同的二叉排序树
[算法8-7] 二叉排序树的建立

相关代码请看配套资源

代码运行结果如下


4.二叉排序树的删除

由于二叉排序树要求关键字之间满足一定的大小关系,这就使得从中删除一个结点的算法相对复杂。具体地,首先在二叉排序树中查找待删结点,如果不存在则不做任何操作;否则,分以下三种情况进行讨论。

①待删结点是叶子结点:

②待删结点只有左子树或只有右子树。

③待删结点既有左子树也有右子树。

先看第①种情况

可以看出,此种情况只需将待删结点的父亲结点的相应孩子城置空,

再来看第②种情况

可以看出,此种情况只需将待删结点的父亲结点的相应孩子域置为该孩子对应的左子树可右子树。

最后来看第③种情况,待删结点既有左子树又有右子树。从二义排序树中删除这样一个 点时,要保持剩下结点之间依然满足排序树的特征,就不能在二义排序树中留下一个空位置,因此需要用另一个结点来补充这个位置。那么应该用哪个结点来补充呢?而日为了保证删除的高效性,应该尽量保证其他元素的移动量是最小的。前面已经分析过,二叉排序树的中序遍历结果恰好是一个有序序列,那么可以利用这个结果找到替换待删结点的位置,其实就是待删结点的前驱结点或后继结点。关键是这两个结点在二叉排序树的什么位置。


综合前面提到的三种情况,算法8-8 为二叉排序树的算法实现。

[算法8-8] 二叉排序树的删除

相关代码请看配套资源

代码运行结果如下

相关文章
|
人工智能 资源调度 Kubernetes
混部开源 Koordinator 背后的故事|学习笔记(一)
快速学习混部开源 Koordinator 背后的故事
混部开源 Koordinator 背后的故事|学习笔记(一)
|
5月前
|
机器学习/深度学习 人工智能 数据处理
我用单张显卡跑了个“法律顾问”,靠它成功追回了加班费
面对劳动纠纷,你是否因法律条款难懂、律师费用高昂而束手无策?本文分享如何用单张显卡本地部署Qwen3-8B模型,结合RAG技术打造专属劳动法AI顾问。相比通用模型,该系统能精准解析加班费争议、证据链构建等实战问题,提供可操作的仲裁策略。从数据处理到服务上线,全流程轻量高效,助力普通人也能“专业维权”。
489 152
|
9月前
|
移动开发 前端开发 JavaScript
鸿蒙NEXT时代你所不知道的全平台跨端框架:CMP、Kuikly、Lynx、uni-app x等
本篇基于当前各大活跃的跨端框架的现状,对比当前它们的情况和未来的可能,帮助你在选择框架时更好理解它们的特点和差异。
905 0
|
编译器 测试技术 C++
【Python 基础教程 01 全面介绍】 Python编程基础全攻略:一文掌握Python语法精髓,从C/C++ 角度学习Python的差异
【Python 基础教程 01 全面介绍】 Python编程基础全攻略:一文掌握Python语法精髓,从C/C++ 角度学习Python的差异
751 0
|
机器学习/深度学习 人工智能 测试技术
PsycoLLM:开源的中文心理大模型,免费 AI 心理医生,支持心理健康评估与多轮对话
PsycoLLM 是合肥工业大学推出的中文心理大语言模型,基于高质量心理数据集训练,支持心理健康评估、多轮对话和情绪识别,为心理健康领域提供技术支持。
3767 51
PsycoLLM:开源的中文心理大模型,免费 AI 心理医生,支持心理健康评估与多轮对话
|
10月前
|
API
揭秘电商API接口:商品订单支付全解析
电商API接口种类繁多,涵盖商品、订单、支付、营销、用户等多方面。常见的有:商品相关(如item_get获取详情、item_search搜索商品)、订单相关(如buyer_order_list获取订单列表)、支付接口(处理支付请求)、营销相关(优惠券、促销活动接口)、用户相关(用户信息、授权、地址管理)及其他常用接口(评价、上下架、购物车管理等)。不同平台提供的API可能有所差异,需参考具体文档。
369 0
|
存储 机器学习/深度学习 计算机视觉
字节开源大模型量化新思路,2-bit量化模型精度齐平fp16
【5月更文挑战第25天】字节跳动研究团队提出新型量化方法decoupleQ,实现2-bit量化模型与fp16/bf16同等精度。该方法通过参数分解,将量化转化为数学优化问题,简化处理并提高硬件兼容性。decoupleQ在大型语音模型上验证了其2-bit量化效果,降低了存储和计算成本,适用于资源受限环境。论文开源,为量化技术发展带来新视角。
712 4
|
机器学习/深度学习 存储 运维
分布式机器学习系统:设计原理、优化策略与实践经验
本文详细探讨了分布式机器学习系统的发展现状与挑战,重点分析了数据并行、模型并行等核心训练范式,以及参数服务器、优化器等关键组件的设计与实现。文章还深入讨论了混合精度训练、梯度累积、ZeRO优化器等高级特性,旨在提供一套全面的技术解决方案,以应对超大规模模型训练中的计算、存储及通信挑战。
828 4
|
JSON 机器人 API
详解如何使用 Python 操作 Telegram(电报)机器人(一)
详解如何使用 Python 操作 Telegram(电报)机器人(一)
5494 9
|
机器学习/深度学习 存储 传感器
深度学习之智能电网优化
基于深度学习的智能电网优化是一种结合先进人工智能技术和电网管理的策略,旨在提高电力系统的效率、稳定性和可持续性。智能电网(Smart Grid)利用深度学习等技术来处理复杂的电力需求数据、生成精准的电力负载预测、优化电力调度、提高故障检测能力,并整合可再生能源资源。
466 2