数据结构入门之查找(七)

  • 概述
    • 常见术语
    • 查找算法的评价指标
  • 一、顺序查找
    • 1.1 算法思想
    • 1.2 算法实现与效率分析
    • 1.3 算法优化的情况
      • 1.3.1 若表中元素有序
      • 1.3.2 若表中元素被查概率不等
  • 二、折半查找
    • 2.1 算法思想
    • 2.2 算法实现与效率分析
    • 2.3 查找判定树
      • 2.3.1 当mid=[(low+high)/2]向下取整
      • 2.3.2 当mid=[(low+high)/2]向上取整
  • 三、分块查找
    • 3.1 算法思想
    • 3.2 算法实现与效率分析
    • 3.3 算法优化
  • 四、B树与B+树
  • 五、散列查找(哈希查找)
    • 5.1 概念
    • 5.2 常见散列函数
      • 5.2.1 除留余数法
      • 5.2.2 直接定址法
      • 5.2.3 数字分析法
      • 5.2.4 平方取中法
    • 5.3 冲突的处理方式
      • 5.3.1 拉链法
      • 5.3.2 开放定址法(三种)
      • 5.3.3 再散列法
    • 5.4 查找效率
  • 总结
  • 代码附录

概述

常见术语

术语 解释
查找 在数据集合中寻找满⾜某种条件的数据元素的过程称为查找
查找表(查找结构) ⽤于查找的数据集合称为查找表,它由同⼀类型的数据元素(或记录)组成
关键字 数据元素中唯⼀标识该元素的某个数据项的值,使⽤基于关键字的查找,查找结果应该是唯⼀的

查找表又分为静态查找表和动态查找表:
静态查找表只需查找符合条件的数据元素(仅关注查找速度即可)
动态查找表还要插入、删除某个数据元素(还要关注插/删操作是否方便)

查找算法的评价指标

平均查找⻓度(ASL, Average Search Length ):所有查找过程中进⾏关键字的比较次数的平均值,反映了查找算法时间复杂度
(通常考虑查找成功、查找失败两种情况下的ASL)
查找⻓度——在查找运算中,需要对⽐关键字的次数称为查找⻓度(散列查找拉链法中空指针的判定不算一次)


一、顺序查找

顺序查找,⼜叫“线性查找”,通常⽤于线性表,时间复杂度为O(n)

1.1 算法思想

从头到尾挨个找(或者从尾到头挨个找)

1.2 算法实现与效率分析

代码实现:

//查找表的数据结构——顺序表
typedef struct{ElemType *elem;      //动态数组的基址int TableLen;      //表的长度
}SSTable;//顺序查找
//法一:找到数组长度末尾为止(从头到尾找,查找表第一个元素下标为0)
int Search_Seq(SSTable ST, ElemType key)
{for(int i = 0; i < ST.TableLen && ST.elem[i] != key; i++){          //查找成功或者没找到时跳出循环}return (i==ST.TableLen? -1 : i);//查找成功时返回元素下标,没找到时返回-1
}//法二:找到哨兵为止((从尾到头找查找表第一个元素下标为1,0存放哨兵)
int Search_Seq(SSTable ST, ElemType key)
{ST.elem[0]=key;                   //在数组第一个(下标为0)位置放置哨兵for(int i = ST.TableLen; ST.elem[i] != key; i--){           //查找成功(找到对应元素)或者没找到(找到哨兵)时跳出循环}return i;    //查找成功时返回对应次序,没找到时返回0
}

查找效率分析:
时间复杂度为O(n)

1.3 算法优化的情况

1.3.1 若表中元素有序

若当前关键字大于(小于)目标关键字时,可以提前判断查找失败

优点
查找失败时ASL更少

用查找判定树分析ASL

成功结点的关键字对比次数 = 结点所在层数
失败结点的关键字对比次数 = 其父结点所在层数

1.3.2 若表中元素被查概率不等

被查概率⼤的放在靠前位置,即可按被查概率降序排列

优点
查找成功时ASL更少

二、折半查找

2.1 算法思想

折半查找,⼜称“⼆分查找”,仅适⽤于有序顺序表
顺序表拥有随机访问的特性,链表没有


low:指向待查数组的最左侧(左闭,low指向的也有可能要查,故更新时low = mid+1)
high:指向待查数组的最右侧(右闭,high指向的也有可能要查,故更新时high = mid -1)
mid:指向待查数组的中间,分为向下取整和向上取整
则判断条件为:low <= high (low>high则查找失败了)

2.2 算法实现与效率分析

代码实现:

//查找表的数据结构——顺序表
typedef struct{ElemType *elem;      //动态数组的基址int TableLen;      //表的长度
}SSTable;//折半查找——增序序列(成功返回数组下标,失败返回-1)
int Binary_Search(SSTable L, ElemType key)
{   //准备指针(下标)int low = 0;int high = L.TableLen - 1;int mid;while(low<=high){mid = (low + high)/2;if(key == L.elem[mid]){return mid;}else if(key < L.elem[mid]){ high = mid - 1;}else       {low = mid + 1;}return -1;}
}

查找效率分析:
由查找判定树可知,树高(不包含失败结点)h = ⌈log2(n + 1)⌉向上取整,查找成功和查找失败的ASL ≤ h ,故折半查找的时间复杂度 = O(log2n)

2.3 查找判定树

折半查找的判定树⼀定是平衡排序⼆叉树,只有最下⾯⼀层是不满的。因此,元素个数为n时树⾼h = ⌈log2(n + 1)⌉向上取整

2.3.1 当mid=[(low+high)/2]向下取整

①如果当前low和high之间有奇数个元素,则 mid 分隔后,左右两部分元素个数相等
②如果当前low和high之间有偶数个元素,则 mid 分隔后,左半部分⽐右半部分⼀个元素
综上:对于任何⼀个结点,必有:右⼦树结点数 - 左⼦树结点数=0或1

该查找判定树构造顺序过程:

失败结点:n+1个(等于成功结点的空链域数量)

2.3.2 当mid=[(low+high)/2]向上取整

①如果当前low和high之间有奇数个元素,则 mid 分隔后,左右两部分元素个数相等
②如果当前low和high之间有偶数个元素,则 mid 分隔后,左半部分⽐右半部分⼀个元素
综上:对于任何⼀个结点,必有:左子树结点数 - 右子树结点数=0或1

该查找判定树构造顺序过程:

三、分块查找


特点:块内⽆序、块间有序

3.1 算法思想

分块查找,⼜称索引顺序查找,算法过程如下:
①在索引表中确定待查记录所属的分块(可顺序、可折半)
②在块内顺序查找

注意:若索引表中不包含⽬标关键字,则折半查找索引表最终停在 low>high,要在low所指分块中查找(low超出索引表范围时也会查找失败)
原因:最终找不到时high=low=mid:若key大于[mid]时,low后移一位,此时[low]>key;若key小于[mid]时,high前移一位,此时还是[low]>key————⽽分块存储的索引表中保存的是各个分块的最⼤关键字

3.2 算法实现与效率分析

索引表中保存每个分块的最⼤关键字和分块的存储区间
代码实现:

//索引表——保存每个分块的最⼤关键字和分块的存储区间
typedef struct {ElemType maxValue;int low, high;
}Index;//顺序表——存储实际元素
ElemType List[100];

查找效率分析:

3.3 算法优化

四、B树与B+树

五、散列查找(哈希查找)

5.1 概念

散列表(Hash Table),⼜称哈希表。是⼀种数据结构。特点是:数据元素的关键字key与其存储地址address直接相关

术语 解释
同义词 不同的关键字通过散列函数映射到同⼀个值
冲突 通过散列函数确定的位置已经存放了其他元素

5.2 常见散列函数

散列函数(哈希函数):Addr=H(key),可以计算⽬标元素存储地址,建⽴“关键字” 与“存储地址” 的联系

设计目标:对给定的关键字集合,应尽可能均匀地散列到各个地址,使不同关键字的冲突尽可能地少

散列查找是典型的“⽤空间换时间”的算法,只要散列函数设计的合理,则散列表越⻓,冲突的概率越低

5.2.1 除留余数法

散列函数:H(key) = key % p
注意:散列表表⻓为m,取⼀个不小于等于m且最接近m的质数p(⽤质数取模,分布更均匀,冲突更少),则可以映射的地址有p个,散列表有m-p个空位置

5.2.2 直接定址法

散列函数:H(key) = key 或 H(key) = a*key + b

它适合关键字的分布基本连续的情况

优缺点:
这种⽅法计算最简单,且不会产⽣冲突。(若关键字分布不连续,空位较多,则会造成存储空间的浪费)

5.2.3 数字分析法

散列函数:选取数码分布较为均匀的若⼲位作为散列地址。

这种⽅法适合于已知的关键字集合(例如:⼿机号后四位作为散列地址)。

优缺点:
若更换了关键字,则需要重新构造新的散列函数。

5.2.4 平方取中法

取关键字的平方值的中间几位作为散列地址。

这种⽅法得到的散列地址与关键字的每位都有关系,因此使得散列地址分布⽐较均匀,适⽤于关键字的每位取值都不够均匀或均⼩于散列地址所需的位数(例如:以“身份证号”作为关键字设计散列函数,可身份证号平⽅取中间5位)

5.3 冲突的处理方式

“冲突”多是避免不了的,“冲突”越多,查找效率越低——而不同的处理冲突的方式可以影响查找效率

5.3.1 拉链法

(⼜称链接法、链地址法)处理“冲突”:把所有“同义词”存储在⼀个链表中

ps:在插⼊新元素时,保持关键字有序,可微微提⾼查找效率(折半查找)

5.3.2 开放定址法(三种)

Hi = (H(key) + di) % m(其中m表示散列表表长,di为增量序列)
所谓开放定址法,是指可存放新表项的空闲地址既向它的同义词表项开放,⼜向它的非同义词表项开放,充分利用哈希表中的空闲位置。

举例:若散列函数为H(key) = key % p,
则哈希函数值域[0,p-1]——(直接映射到哈希表的范围)
冲突处理函数值域[0,m-1]——(可以填充满哈希表)
哈希表中可以多m-p个位置来存放关键字,使冲突降低

1. 线性探测法
对于Hi = (H(key) + di) % m,当di= 0, 1, 2, 3, …, m-1时,称为线性探测法。(即发⽣冲突时,每次往后探测相邻的下⼀个单元是否为空)

对于H(key)=key % p ,若取质数p=13,表长m=16,则
①映射14时:H(key)=14%13=1,此时发生第0次冲突(即没有冲突),得到的哈希地址为H0=(1+ d0)%16= 1
②映射1时:H(key)=1%13=1,会发生1次冲突,重新计算得到的哈希地址为H1= (1+ d1) % 16 = 2
③映射27时:H(key)=27%13=1,会发生3次冲突,重新计算得到的哈希地址为H3= (1+ d3) % 16 = 4
④一直映射到结束…
(即发⽣冲突时,每次往后探测相邻的下⼀个单元是否为空)
注意:映射时%p,重新计算处理冲突时%m

优缺点:

  • 线性探测法很容易造成同义词、⾮同义词的“聚集(堆积)”现象,严重影响查找效率,因为冲突后再探测⼀定是放在某个连续的位置

2. 平方探测法
对于Hi = (H(key) + di) % m,当di = 02, 12, -12, 22, -22, …, k2, -k2时,称为平方探测法,⼜称⼆次探测法,其中k ≤ m/2。
(注意负数的取模,见最后)

优缺点:

  • 比起线性探测法更不易产⽣“聚集(堆积)”问题
  • 散列表长度m必须是⼀个可以表示成4j + 3的素数,才能探测到所有位置

3. 线性探测法
对于Hi = (H(key) + di) % m,当di是⼀个伪随机序列,如 di = 0, 5, 24, 11, …,称为伪随机序列法。

4. 查找和删除的ASL分析
查找:同义词、⾮同义词都需要被检查(从映射的位置开始到表尾)

注意1:空位置的判断也要算作一次比较
注意2:越早遇到空位置,就可以越早确定查找失败

删除:采⽤“开放定址法”时,删除结点不能简单地将被删结点的空间置为空,否则将截断在它之后填入散列表的同义词结点的查找路径(查找的注意2)——可以做⼀个“删除标记”,进⾏逻辑删除

5.3.3 再散列法

除了原始的散列函数 H(key) 之外,多准备⼏个散列函数,当散列函数冲突时,⽤下⼀个散列函数计算⼀个新地址,直到不冲突为⽌:Hi = RHi(Key) (i=1,2,3….,k)

5.4 查找效率

取决于散列函数、冲突的处理方式、装填因子α
装填因⼦α=表中记录数/散列表⻓度(会直接影响散列表的查找效率)

总结

取模或取余计算公式:

第一步:求整数商c:
①进行求模运算c = [a/b] = -7 / 4 = -2(向负无穷方向舍入),
②进行求余运算c = [a/b] = -7 / 4 = -1(向0方向舍入);
第二步:计算模和余数的公式相同,但因c的值不同,
①求模时:r = a - c*b =-7 - (-2)4 = 1,
②求余时:r = a - c
b = -7 - (-1)*4 =-3。

代码附录

《王道》数据结构之查找(七)相关推荐

  1. 数据结构笔记(王道考研) 第七章:查找

    大部分内容基于中国大学MOOC的2021考研数据结构课程所做的笔记,该课属于付费课程(不过盗版网盘资源也不难找...).后续又根据23年考研的大纲对内容做了一些调整,将二叉排序树和平衡二叉树的内容挪到 ...

  2. 数据结构实验之查找七:线性之哈希表

    数据结构实验之查找七:线性之哈希表 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 根据给定 ...

  3. SDUT 3379 数据结构实验之查找七:线性之哈希表

    数据结构实验之查找七:线性之哈希表 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 根据给定的一系列整数关键字和素数p, ...

  4. 【除留余数法定义hash函数+线性探测法解决hash冲突】数据结构实验之查找七:线性之哈希表

    Think: 1知识点:除留余数法定义hash函数+线性探测法解决hash冲突 数据结构实验之查找七:线性之哈希表 Time Limit: 1000MS Memory Limit: 65536KB P ...

  5. 王道数据结构与算法:完整笔记

    王道数据结构与算法:完整笔记 文章目录 数据结构笔记 第一章 绪论 1.1 基本概念 1.2 数据结构三要素 1.3 算法的概念 1.4 算法效率的度量 第二章 线性表 2.1 线性表的定义和基本操作 ...

  6. 20172301 《程序设计与数据结构》第七周学习总结

    20172301 <程序设计与数据结构>第七周学习总结 教材学习内容总结 二叉查找树是一种含有附加属性的二叉树,其左孩子小于父结点,父结点小于或者等于右孩子. 用链表实现二叉查找树 add ...

  7. 20172332 2017-2018-2 《程序设计与数据结构》第七周学习总结

    20172332 2017-2018-2 <程序设计与数据结构>第七周学习总结 教材学习内容总结 第十一章 二叉查找树 1.二叉查找树:一种带有附加属性的二叉树.即每个结点其左孩子都要小于 ...

  8. 23王道数据结构代码题全解(二)

    计划更新23王道数据结构所有课后代码习题的实现,虽然考试写的一般都是伪代码,但是强迫症的我还是全部实现了一遍,仓库在这里 代码全部是用 C++ 写的,都可以编译运行,包含暴力解和最优解. 持续更新,目 ...

  9. 王道数据结构代码——线性表

    目录 0. 前言 1. 顺序表 2. 单链表--不带头结点 3. 单链表--带头结点 4. 双链表--带头结点 5. 循环单链表--L指向表头 6. 双链表--L指向表尾 7. 循环双链表 8. 静态 ...

  10. 23王道数据结构代码题全解(三)

    计划更新23王道数据结构所有课后代码习题的实现,虽然考试写的一般都是伪代码,但是强迫症的我还是全部实现了一遍,仓库在这里 代码全部是用 C++ 写的,都可以编译运行,包含暴力解和最优解. 持续更新,目 ...

最新文章

  1. Azkaban入门(启动一个Simple Example)
  2. 带你了解走出数据治理第一步 ,数据资产分类分级
  3. python程序文件扩展名有_python程序文件的扩展名称是什么
  4. XCTF-MISC-新手区:give_you_flag
  5. 【和60】软件即服务的三重境界
  6. 【番外】线性回归和逻辑回归的 MLE 视角
  7. 利用awstats分析nginx日志 简单配置
  8. Lua,Lua API,配置文件
  9. 新鲜出炉的自主协同操作系统研讨会纪要
  10. 小i机器人伴侣_【数据分析】2020年3月全国工业机器人产量统计数据分析
  11. .net微信开发吐血总结
  12. 数据管理管的是哪些方面
  13. 实录分享|一篇文章看CNTV的容器化探索和平台搭建
  14. Olly Advanced 1.27
  15. java实验学校与教师_java实验
  16. 企业未来的发展机遇,或许在直播中
  17. CCF 201712-4行车路线
  18. 数字示波器中单位:Kpts, PPM, Sa/s, wfs
  19. 417. 太平洋大西洋水流问题(medium) -力扣(leetCode)逆流而上,JS图的深度优先遍历算法
  20. 南非、马来西亚和印度尼西亚比欧洲对数字货币的熟悉程度更高

热门文章

  1. 浮点数的存储,规格化
  2. JavaSE(面向对象、继承、抽象类)
  3. STM32F0 没有BOOT0 复位按键失效 程序运行一半
  4. 元宇宙体育俱乐部 #kodeclubs 基于threejs的网页版虚拟空间
  5. 【转载】高通msm8996平台的ASOC音频路径分析(基于androidN及linux3.1x)
  6. 零基础如何学数据恢复
  7. 熟悉 Linux 命令运维工程师必备
  8. sourceTree推送报错Unknown author
  9. css如何自己制作图标字体以及其使用方法
  10. 学校计算机室发生火灾自救,强化师生消防意识 筑牢学校安全堡垒