查找(顺序查找、二分查找、插值查找)_插值查找什么时候坏-程序员宅基地

技术标签: 算法  顺序查找  二分查找  优化  插值查找  算法与数据结构  


查找定义 :根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)。
查找表分类:静态查找表和动态查找表。
动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。

1. 顺序查找

/* 顺序查找,a为数组,n为要查找的数组元素个数,key为要查找的关键字*/
int Sequential_Search(int *a, int n, int key)
{
     int i = 0;
     for(; i < n; i++)
     {
          if(a[i] == key)
               return i;
     }
     return 0;
}

/*有哨兵顺序查找-优化最简单的顺序查找*/
/*优化部分:a[0]存放要查找的关键字key,减少了数组越界的比较,如果查找表长度很大,还是比最简单的顺序查找快很多的。ps:a[0] = key的目的就是让上述的简单顺序查找的两次判断修正为一次a[i]与a[0]是否相等的一次判断。*/
int Sequential_Search2(int *a, int n, int key)
{
     int i;
     a[0] = key;
     for(i = n; a[i] != a[0]; i--);
     return i;
}
  平均查找长度 (Average Search Length,ASL)
     需和指定key进行比较的关键字的个数的期望值,成为查找算法在查找成功时的平均查找长度。
     对于含有n个数据元素的查找表,查找成功的平均查找长度为:ASL = Pi*Ci的和。
     Pi:查找表中第i个数据元素的概率。
     Ci:找到第i个数据元素时已经比较过的次数。
     顺序查找 查找 成功 时的平均查找长度为:
     (假设每个数据元素的概率相等) ASL = 1/n(1+2+3+…+n) = (n+1)/2 ;
     当查找 不成功 时,需要n+1次比较,时间复杂度为O(n);


2. 有序表查找


2.1 折半查找

前提:线性表中的记录必须是关键字有序(通常从小到大),线性表必须采用 顺序存储。
基本思想:取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录左半区继续查找;否则,在右半区查找。不断重复,知道查找成功或者查找失败为止。
/*折半查找,非递归*/
int Binary_Search(int *a, int n, int key)
{
     int low, high;
     int mid;
     low = 1;
     high = n;
    
     while(low < high)			//可以有等号 low < = high ,不影响判断。
     {
          mid = (low + high) / 2; 	//可以修正为 mid = (low + high) >> 1;
          if(a[mid] == key)
               return mid;
          if(a[mid] > key)
               high = mid - 1;
          if(a[mid] < key)
               low = mid + 1;		//对于if else语句,可以考虑条件表达式,减少代码的行数。
     }
     return 0;
}
/*折半查找,递归实现*/
int Binary_Search2(int *a, int low, int high, int key)
{
     int mid = (low + high) / 2;
     if(a[mid] == key)
          return mid;
     if(a[mid] > key)
          return Binary_Search2(a, low, mid-1, key);     //有没有return都可以。
     else
          return Binary_Search2(a, mid+1, high, key);     //有没有return都可以。
}
/*      ASL: “具有n个节点的完全二叉树的深入为[log2n]+1,尽管折半查找判定二叉树不是完全二叉树,但同样相同的推导可以得出,
               最坏情况是查找到关键字或查找失败的次数为[log2n]+1”,最好情况是1,所以折半查找的时间复杂度为o(logN)>o(n).

     注:折半查找的前提条件是需要有序表顺序存储,对于静态查找表,一次排序后不再变化,这样的算法已经比较好了。但对于需要
     频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用。——《大话数据结构》
*/

2.2 插值查找

首先考虑一个新问题,为什么一定要是折半,而不是折四分之一或者折更多呢?
打个比方,在英文字典里面查“apple”,你下意识翻开字典是翻前面的书页还是后面的书页呢?如果再让你查“zoo”,你又怎么查?很显然,这里你绝对不会是从中间开始查起,而是有一定目的的往前或往后翻。
同样的,比如要在取值范围1 ~ 10000 之间 100 个元素从小到大均匀分布的数组中查找5, 我们自然会考虑从数组下标较小的开始查找。
经过以上分析,折半查找这种查找方式,还是有改进空间的,并不一定是折半的!
mid = (low+high)/ 2, 即 mid = low + 1/2 * (high - low);
改进为下面的计算机方案(不知道具体过程):mid = low + (key - a[low]) / (a[high] - a[low]) * (high - low),也就是将上述的比例参数1/2改进了,根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。

分析:从时间复杂度上来看,它也是o(n),但是对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么差值查找未必是很合适的选择。

2.3 斐波那契查找

原理:利用斐波那契数列的性质,黄金分割的原理来确定mid的位置。
代码如下:
/*斐波那契 查找*/
int Fbonacci_Search(int *a, int n, int key)
{
	int low,high,mid,i,k;
	int F[] = {0,1,1,2,3,5,8,13,21,34};		//经典的斐波那契数列已经早就定义好,也可以递归自己求解。
	low = 1;
	high = n;
	k = 0;
	while(n > F[k] - 1)	//计算 n 位于斐波那契数列的位置
		k++;
	for(i=n; i<F[k] - 1; i++)	//将不满的数值补全
		a[i] = a[n];
	while(low <= high)
	{
		mid = low + F[k-1] - 1;			//利用斐波那契数列来找寻下一个要比较的关键字的位置
		if(key < a[mid])
		{
			high = mid - 1;
			k--;
		}
		else 
		{
			if(key > a[mid])
			{
				low = mid + 1;
				k = k -2;
			}
			else
			{
				if(mid <= n)
					return mid;
				else
					return n;
			}
		}
	}
}

总结:
折半查找进行加法与除法运算(mid = (low + high) / 2),插值查找进行复杂的四则运算( mid = low + (key - a[low] / (a[high] - a[low]) * (high - low)) ),二斐波那契查找只是运用简单家减法运算 (mid  = low + f[k-1] -1) ,在海量的数据查找过程中,这种席位的差别会影响最终的查找效率。三种有序表的查找本质上是分割点的选择不同,各有优劣,实际开发可根据数据的特点综合考虑再做决定。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wangyunyun00/article/details/23464359

智能推荐

vs2012配置python_安装适用于Visual Studio 2012(PTVS)的Python工具-程序员宅基地

文章浏览阅读220次。I installed the version for VS 2010 a few weeks ago, and it went flawlessly. I am not having such good luck for the version for VS 2012. What did I do wrong? I ran, in order, two installers from Micro..._vs2012 installing a python interpreter will let you run your project, and wi

无线网络安全与解决方案(Wireless Security)_wifi security-程序员宅基地

文章浏览阅读9k次,点赞9次,收藏44次。讲解几种无线网络安全问题(Wireless Security)以及相应的解决方案。_wifi security

【基础知识】tiff格式图片介绍及读取-程序员宅基地

文章浏览阅读2.3w次,点赞26次,收藏121次。TIFF格式也叫TIF,属于位图格式,全名是。TIFF格式和JPEG及PNG都是属于一种主流且高位彩色图像格式。使用过扫描仪或传真的小伙伴应该还记得,很多品牌及其默认文件就是.tiff后缀。因为TIFF最初的设计目的就是在1980年左右,扫描仪厂商达成一个公用的统一的扫描图像文件格式,而不是每个厂商使用自己专有的格式造成混乱。TIFF是一种比较灵活的图像格式,它的全称是Tagged Image File Format,文件扩展名为TIF或 TIFF。_tiff格式

【干货】谈谈交互中容易混淆的各种“流”(产品经理必看)_节点交互与任务交互的区别-程序员宅基地

文章浏览阅读231次。当我决定想以最容易理解的方式来写一篇关于UX设计流程的文章时,我注意到了一个严重的问题——有的时候设计过程不符合一条单一的逻辑流线。 但是同一个工具怎么会同时有用却又难以理解呢? 所以我阅读了更多相关的内容,我开始慢慢理解。 在本文中,我将讨论从“流程图”到“用户流”的许多不同类型的视觉化图表之间的区别,也借这个机会浅谈为什么它会被这么多人误解。开始之前,我想先说明“流”(flow)这个术语在文中用来表示具有某种顺序或方向的图表。摹客支持PRD文档在线撰写、全貌画板和流程图模式,与富交互原型深度结合.._节点交互与任务交互的区别

Android Studio模拟器如何运行apk文件_android stodio怎么打开一个apk文件目录-程序员宅基地

文章浏览阅读2.1w次,点赞12次,收藏73次。Android手机软件都是以apk为后缀的,而apk文件在电脑中是无法直接运行的,需要在模拟器中安装和打开。如果使用Android Studio中的模拟器,首先需要打开自己的AVD模拟器。将apk文件复制到android studio安装目录里的androidsdk\platform-tools文件夹下(如下图所示文件夹)win+R运行cmd终端,运用指令cd切换到adb.exe所在文件夹下C:\Users\Lenovo>d:D:\>cd andriodD:\andriod&g_android stodio怎么打开一个apk文件目录

整数转换成固定位数的字符串_java固定位数字符串-程序员宅基地

文章浏览阅读3.6k次,点赞2次,收藏4次。int youNumber = 1; // 0 代表前面补充0 // 4 代表长度为4 // d 代表参数为正数型 String str = String.format("%04d", youNumber); System.out.println(str); // 0001_java固定位数字符串

随便推点

qml红色下划线去除_qt 的qml文件中总是红色错误线-程序员宅基地

文章浏览阅读750次。红色下划线_qt 的qml文件中总是红色错误线

Kafka Consumer 执行 poll 操作时获取空数据的原因分析(2)_consumer.poll获取不到数据-程序员宅基地

文章浏览阅读6.1k次。上一个问题的回答:https://blog.csdn.net/weixin_37392582/article/details/1046851911. 场景当用户需要获取数据时,后端从指定 topic 下消费1条数据;每次操作的间隔为 0~;每个 topic 只有一个 消费者。2. 现象多个topic同时消费数据,经常出现“获取数据失败”的问题;所有的消费者均在一个 group 内。..._consumer.poll获取不到数据

SCRATCH 小游戏-程序员宅基地

文章浏览阅读6k次。scratch 小游戏《五连子》

修改caffe中resize的方式_caffe resize-程序员宅基地

文章浏览阅读824次。opencv的resize默认的是使用双线性插值INTER_LINEAR,也可以是尝试其他的方式进行插值操作if (param.random_interpolation_method()) { // 0: INTER_NEAREST // 1: INTER_LINEAR // 2: INTER_CUBIC // 3: INTER_AREA // 4: IN..._caffe resize

vue路由URL拼接全局参数_vue 在路由拦截里,给url加参数-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏6次。vue-router地址栏URL拼接全局参数业务需求:vue单页面项目,需要在URL地址栏拼接固定的参数,在所有页面使用。默认采用vue路由的history模式;hash模式也可以同样适用。主要是在 vue-router 导航守卫的全局前置守卫中实现。即 vue-router 生命周期钩子函数 router.beforeEach 中进行路由拦截处理。主要代码:router.beforeEach((to, from, next) => { if (Object.keys(to.quer_vue 在路由拦截里,给url加参数

区块链学习笔记:DAY05 如何使用公有云区块链服务-程序员宅基地

文章浏览阅读1.3k次。这是最后一节课了,主要讲华为云在云区块链提供的服务,如何基于华为云BCS来构建应用先来个简单的比喻:1.有关BaaS的范围定义包含物理主机、虚拟主机、容器服务、区块链、智能合约和服务2.华为云区块链服务BCS3.华为云BCS特点:1)易用:一键部署,区块链系统全生命周期管理2.易用:可视化智能合约生命周期管理,多语言支持3)高可用:成员动态准入,..._公有云区块链服务

推荐文章

热门文章

相关标签