查找(顺序查找,折半查找,分块查找)
查找
查找是在查询数据过程中必不可少的一个环节,那么如何来进行查找,以及如何进行高效率的查找,就是接下来要解决的问题。
文章目录
- 查找
- 查找的概念
- 顺序查找
- 顺序查找-算法原理
- 顺序查找-算法实现
- 顺序查找-性能分析
- 折半查找
- 折半查找-算法原理
- 折半查找-算法实现
- 折半查找-性能分析
- 分块查找
- 分块查找-算法原理
- 分块查找-算法实现
- 分块查找-性能分析
查找的概念
首先提出一些定义,在查找元素的时候,必然是从一堆数据中去找自己想要的数据,那么这种由同一类型的数据元素构成的集合就被称为查找表。对于查找表可执行以下操作:
- 查询某个“特定的”数据元素是否在查找表中;
- 检索某个“特定的”数据元素的各种属性;
- 在查找表中插入一个数据元素;
- 从查找表中删去某个数据元素
根据查找表的操作方式来分类,可以将查找表分为两类:静态查找表和动态查找表。
- 静态查找表:仅作查询和检索的查找表。
- 动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已存在的某个数据元素
在数据表中,应该用一个东西来标识每一个数据元素用以识别该数据,这个东西就叫做关键字,关键字是数据元素(或记录)中某个数据项的值,用以标识(识别)一个数据元素(或记录)。其中关键字也可以进行细分:
- 主关键字:可以识别唯一的一个记录的关键字
- 次关键字:能识别若干记录的关键字
关于关键字这里可以简单举个例子,以全校成绩为例,学号就是每个学生的主关键字,因为一个学号唯一识别一个学生,学院就是次关键字,学院可以识别若干个学生,但是不能唯一识别一个学生。
那么查找是一个什么过程呢,查找就是根据给定的值,在查找表中确定一个其关键字等于给定值的数据元素那么根据查询结果来看分为查询成功和查询不成功,查找成功就是在查找表中查找到指定的记录;查找不成功就是在查找表中没有找到指定记录。
最后还有很关键的一点,就是如何衡量一个查找算法的效率。这里主要参考以下几点:
- 时间复杂度
- 空间复杂度
- 平均查找长度ASLASLASL
这里重点讲一下什么是平均查找长度ASLASLASL,平定义为确定记录在表中的位置所进行的和关键字比较的次数的平均值,可以使用下面的公式来表示。
ASL=∑i=1nPiCiASL=\sum_{i=1}^n{P_iC_i} ASL=i=1∑nPiCi
其中,nnn为查找表的长度,即表中所含元素。PiP_iPi为查找第iii哥元素的概率(∑Pi=1\sum{P_i=1}∑Pi=1)。CiC_iCi是查找第iii个元素时同给定值KKK比较的次数。
顺序查找
顺序查找算法是查找算法中最简单直接的一种方法,它是属于静态查找表中的一种查找方法。
顺序查找-算法原理
顺序查找有两种实现方式,第一种就是平时常用的那种,从数组的第一个元素开始往后遍历,直到遍历到数组的最后一个元素或者说找到对应的元素才停止。
对于第一种方式,在查找的时候还需要考虑数组是否会越界的问题,那么为了解决这个问题,就采用哨兵的方式来进行查找。一般情况将数组的第一个元素当作哨兵,放置待查询关键字,然后从数组的最后一个元素开始往前查找,这样一来就不用考虑越界的情况,因为数组的第一个元素一定会等于最后一个元素。
顺序查找-算法实现
对于第一种方法,直接采用最简单的forforfor循环从第一个遍历到最后一个即可,代码如下所示。
// 顺序查找,不带哨兵
int Sequential_Search(int data[], int n, int key)
{// 通过for循环查找即可for (int i = 1; i <= n; i++){// 如果相等则返回下标if (data[i] == key)return i;}// 没有找到返回0return 0;
}
对于第二种哨兵法,整体思路和前面相同,都是通过遍历的方法来进行查找,但是减少了数组越界的判断。
// 顺序查找,带哨兵
int Sequential_Search2(int data[], int n, int key)
{data[0] = key; // 第一个位置当作哨兵int index = n; // 从后往前进行查找// 此处省略对数组越界的判断,因为data[0] = keywhile (data[index] != key){index--;}return index;
}
顺序查找-性能分析
对于顺序表而言,Ci=n−i+1C_i=n-i+1Ci=n−i+1,同时假设是等概率查找,即Pi=1nP_i=\frac{1}{n}Pi=n1,故可以计算得到该顺序表的平均查找长度为
ASL=nP1+...+Pn=n(n+1)2ASL=nP_1+...+P_n=\frac{n\left( n+1 \right)}{2} ASL=nP1+...+Pn=2n(n+1)
同时顺序查找的优点在于其简单且适应面广(对表没有额外要求),但是缺点也很明显,平均查找长度较大,尤其是当nnn很大时,查找效率很低。
折半查找
在平常查找过程中,例如在图书馆找某本书,如果图书馆中的书已经按照书名的首字母拼写摆放好了,那么就可以根据这些顺序来更加快速的找到想要的书籍,而折半查找就是源于这一思想,同时折半查找也是静态查找表中的查找方法。
折半查找-算法原理
首先需要声明的是,折半查找算法是有序表的查找方法,这也就代表在折半查找算法中,静态查找表按关键字大小的次序,有序地存放在顺序表中。
在有序表中,可以通过不断缩小区间的方法来确定要查找的元素的位置,原理类似于数学中的牛顿二分法。每次查找时根据区间的上下界,计算出一个中点值,该中点值的关键字如果大于查找的关键字,则取后半部分区间继续查找,反之则取前半部分区间继续查找,直到找到该记录或区间无法再缩小为止(找不到)。
接下来查看一个查找成功的例子。
以及一个查找不成功的例子。
折半查找-算法实现
具体到代码实现时,只需要将上述步骤设计成流程即可,如下所示。
- nnn个对象从小到大存放在有序顺序表STSTST中,kkk为给定值
- 设lowlowlow、highhighhigh指向待查元素所在区间的下界、上界,即low=1,high=nlow=1, high=nlow=1,high=n
- 设midmidmid指向待区间的中点,即mid=(low+high)/2mid=(low+high)/2mid=(low+high)/2
- 让kkk与midmidmid指向的记录比较
若k=ST[mid].keyk=ST[mid].keyk=ST[mid].key,查找成功
若k<ST[mid].keyk<ST[mid].keyk<ST[mid].key,则high=mid−1high=mid-1high=mid−1 [上半区间]
若k>ST[mid].keyk>ST[mid].keyk>ST[mid].key,则low=mid+1low=mid+1low=mid+1 [下半区间] - 重复3,4操作,直至low>high时,查找失败。
// 折半查找
int Binary_Search(int data[], int n, int key)
{int low = 1; // 低位int high = n; // 高位while (low <= high) // 不断缩小区间{int mid = (low + high) / 2; // 中值if (data[mid] > key) // 如果中值大于关键字,则下一次从[low, mid-1]中找high = mid - 1;else if (data[mid] < key) // 如果中值小于关键字,则下一次从[mid+1,high]中找low = mid + 1;else // 成功找到则直接返回return mid;}// 没有找到返回0return 0;
}
折半查找-性能分析
在分析折半查找的平均查找长度时,需要借助到二叉树来进行分析,由于每次都是折半的过程,那么就可以将原来的有序序列看做成一个二叉树(中序遍历为有序结果),如下所示。
在具体到查找时,也是按照上述顺序逐层进行查找,因为有nnn个结点的上述二叉树深度为[log2n]+1[\log _{2}^{n}]+1[log2n]+1,故折半查找法在查找过程中比较次数不超过[log2n]+1[\log _{2}^{n}]+1[log2n]+1次。上述二叉树也被称为判定树。
设有序表的长度n=2h−1n=2^h-1n=2h−1,则描述折半查找的判定树是深度为hhh的满二叉树。树中层次为1的结点有1个,层次为hhh的结点有2h−12^{h-1}2h−1个。
假设表中每个记录的查找概率相等,则查找成功时折半查找的平均查找长度为
ASL=1n∑i=1nCi=1n[∑j=1hj⋅2j−1]=n+1nASL=\frac{1}{n}\sum_{i=1}^n{C_i=}\frac{1}{n}\left[ \sum_{j=1}^h{j\cdot 2^{j-1}} \right] =\frac{n+1}{n} ASL=n1i=1∑nCi=n1[j=1∑hj⋅2j−1]=nn+1
折半查找的优点在于相比于顺序查找,其查找效率很高,特别是在静态查找表的长度很长时,但是缺点也很明显,那就是只能适用于有序表,且数据需要以顺序存储结构存储。
分块查找
分块查找是一种索引顺序表查找方法,是折半查找和顺序查找的简单结合。索引顺序表是将整个表分成几块,块内无序,块间有序,这两个要求的含义具体如下所示。
- 块内无序:同一块内的记录不要求有序。
- 块间有序:后一块中所有记录的关键字均大于前一块中所有记录的最大关键字。
分块查找-算法原理
那么对于索引顺序表(分块有序表)可以将其分为两个表,第一个表就是主表,用数组存放待查记录,每个数据元素至少含有关键字域;第二个就是索引表,每个结点含有最大关键字域和只想本快第一个结点的指针,如下所示。
那么在查找某个元素时,首先通过折半查找的方法来索引块中找到该元素所属的块,之后再通过顺序查找的方式在该块中找到对应的元素。
分块查找-算法实现
分块查找主要就是折半查找和顺序查找的合并版,实现起来没有问题,重点在于如何建立索引表。这里的前提是已经分好了块,但是需要去标注每个块的起始下标,直接通过遍历主表元素即可,从第一个块的最大元素比较起,遇到的第一个比第一个块最大元素还大的元素,就是第二个块的起始元素,以此类推。
// 创建索引表
void Create_Index(int data[], int n, int index[], int indexOrder[])
{/// <summary>/// 创建索引表/// </summary>/// <param name="data">查找表</param>/// <param name="n">查找表长度</param>/// <param name="index">每个块的最大数据</param>/// <param name="indexOrder">每个块的起始下标</param>int pos = 1; // 数据从1开始indexOrder[pos++] = 1; // 第一组起点为1for (int i = 1; i <= n; i++) // 遍历所有数据{if (data[i] > index[pos - 1]) // 遇到第一个比前一个块最大数据大的{indexOrder[pos++] = i; // 说明该点是一个新块的起点}}
}
这里为了简单起见,块的查找和块内查找都是使用了顺序查找进行实现,代码如下所示。
// 分块索引查找
int Index_Search(int data[], int n, int index[], int k, int indexOrder[], int key)
{/// <summary>/// 分块索引查找/// </summary>/// <param name="data">查找表</param>/// <param name="n">查找表长度</param>/// <param name="index">每个块的最大数据</param>/// <param name="k">块的个数</param>/// <param name="indexOrder">每个块的起始下标</param>/// <param name="key">待查找的关键字</param>/// <returns></returns>// 先找数据所属的块int part = 1; // 从第一块开始查找int begin = 0; // 数据所属块的起始下标for (int i = 1; i <= k; i++) // 逐块查找{// 如果当前块最大数据大于带查找关键字// 则说明关键字就在该块中if (index[i] >= key) {part = i;begin = indexOrder[i];break;}}int end = n; // 初始化块的尾地址if (part < k) // 判断是否是最后一个块end = indexOrder[part + 1] - 1;// 顺序查找for (int i = begin; i <= end; i++){if (data[i] == key)return i;}return 0;
}
分块查找-性能分析
对于分块查找,设若将长度为nnn的表分成bbb块,每块含sss个记录,并设表中每个记录查找概率相等。
用折半查找方法在索引表中查找索引块
ASL块间≈log2ns+1ASL_{\text{块间}}\approx log_{2}^{\frac{\mathrm{n}}{\mathrm{s}+1}} ASL块间≈log2s+1n
用顺序查找方法在主表对应块中查找记录
ASL块内≈s2ASL_{\text{块内}}\approx \frac{\mathrm{s}}{2} ASL块内≈2s
故总的平均查找长度为
ASL≈log2ns+1+s2ASL\approx log_{2}^{\frac{\mathrm{n}}{\mathrm{s}+1}}+\frac{\mathrm{s}}{2} ASL≈log2s+1n+2s
查找(顺序查找,折半查找,分块查找)相关推荐
- php分块查找,索引查找(索引查找、分块查找) C语言实现
1.基本概念 索引查找又称分级查找. 索引存储的基本思想是:首先把一个集合或线性表(他们对应为主表)按照一定的函数关系或条件划分成若干个逻辑上的子表,为每个子表分别建立一个索引项,由所有 这些索引项构 ...
- (王道408考研数据结构)第七章查找-第二节3:分块查找
文章目录 一:分块查找基本思想 二:注意问题 三:效率分析 一:分块查找基本思想 分块查找:我们可以对数据集进行分块,使其分块有序,然后再对每一块建立一个索引项.分块有序具体是指 块内无序: 也即块内 ...
- 设计一个程序,在顺序表中采用分块查找发查找关键字。
#include"iostream" using namespace std; class list{ private:int data[30];int length;int ke ...
- 408数据结构学习笔记——顺序查找、折半查找、分块查找
目录 1.顺序查找 1.1.顺序查找的概念 1.2.顺序查找的代码 1.3.顺序查找的查找效率 1.4.顺序查找的优化 1.5.顺序表的时间复杂度 2.折半查找 2.1.折半查找的概念 2.2.折半查 ...
- 查找——顺序、二分法、斐波那契、插值、分块
目录 创建随机数列 顺序查找 二分法查找 斐波那契查找 插值查找 分块查找 创建随机数列 import random import timeit #创建随机的数列,n为数列个数,数列元素在0-1000 ...
- 数据结构之查找算法:分块查找
查找算法:分块查找 思维导图: 分块查找的定义: 如何分块: 如何查找: 代码实现: 查找效率: 思考: 思维导图: 分块查找的定义: 如何分块: 例: 如何查找: 1.先查找在哪块 2.然后查找块内 ...
- 【数据结构-查找】1.通俗易懂讲解 —— 顺序-折半-分块查找
平均查找长度是所有查找过程中进行关键字的比较次数的平均值 失败的平均查找长度:图画出来,补充为二叉树,其中补上的层数之和是分子,列表长度 len+1 是分母 顺序查找 顺序查找主要是用于线性表中的查找 ...
- 常用查找算法(顺序、折半、二叉树、哈希、分块)介绍
一.顺序查找 条件:无序或有序队列. 原理:按顺序比较每个元素,直到找到关键字为止. 时间复杂度:O(n) 二.二分查找(折半查找) 条件:有序数组 原理:查找过程从数组的中间元素开始,如果中间元 ...
- 数据结构之查找-顺序查找,折半查找,分块查找
文章目录 一.顺序查找 1.1 无序表的顺序查找 1.2 有序表的顺序查找 二.折半查找 三.分块查找 一.顺序查找 顺序查找又称线性查找,主要用于在线性表中进行查找. 1.1 无序表的顺序查找 对无 ...
- 顺序查找、折半查找、分块查找
目录 一.查找的基本概念 二.顺序查找 一般线性表的顺序查找 有序线性表的顺序查找 三.折半查找 折半查找的算法 判定树 四.分块查找 一.查找的基本概念 查找:在数据集合中寻找满足某种条件的数据元素 ...
最新文章
- 不自动切换eclipse视图
- Git复习(十)之常见报错和疑问
- 让企业零成本拥有自己独立的企业网站不再是问题...
- JAVA面试题(part1)--变量相加与常量相加
- java环境变量的配置步骤,实现原理分析
- 【HDU - 5943】Kingdom of Obsession(数论,素数间隔结论,构造,思维,匈牙利算法,匹配问题)
- 服务器宕机不再愁!Docker 内置功能帮您解决
- 【Flink】did not rea ct to cancelling signal for 30 seconds, but is stuck No implementation was bound
- 【Divided Two】cpp
- [转]有关TinyXML使用的简单总结
- SVN_SERVER的搭建
- 自动采集收录导航源码
- HC05蓝牙模块特点及使用介绍
- 在html中函数无效,无法在按钮点击HTML中调用JavaScript函数
- 二分算法:数的三次方根
- c#程序员和java薪酬_语言趋势和薪水:Java的未来之路
- emacs命令/edwin参考
- 教你怎么免费升级宝塔企业版和专业版
- linux双线路由,双线机房双IP linux设置路由
- 什么是ISO 21434?给汽车软件开发人员的合规贴士