查找算法(4个简单的算法)

5 篇文章 0 订阅
4 篇文章 0 订阅

查找:

查找表:由同一类型的数据元素(或数据)构成的集合。

关键字是数据元素中某项数据项的值,又称为键值。

如果此关键字可以唯一的表示一个记录,则称此关键字为主关键字(类此数据库中primary key主键)

对于那些可以识别多个数据元素(或记录)的关键字,我们称为次关键字。

查找:根据给定的某个值,在查找表中确定一个其关键字等于给定的数据元素(或记录)。如果表中不存在关键字等于给定的记录,则称为查找不成功,此时查找的结果可以给出一个空记录或空指针。

 

1.查找表按照操作方式可以分为:静态查找表和动态查找表。

静态查找表:只做查找操作的查找表。它的主要操作时:

(1)      查询某个“特定的”数据元素是否在查找表中

(2)      检索某个特定的数据元素和各种属性。

 

动态查找表:在查找的过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。

(1)      查找时插入数据元素

(2)      查找时删除数据元素

 

面向查找的数据结构称为查找结构。

 

顺序查找:sequential search 又叫线性查找,是基本的查找技术,它的查找过程是:从表中第一个(或最后一个)记录开始,逐个进行记录的关键字和给定值比较,如果某个记录的关键字和给定值相等,则查找成功,找到所查的记录;如果知道最后一个(或第一个),其关键字和给定值比较都不等时,则表中没有所查的记录,查找不成功。

顺序表查找算法:

Int sequential_search(int *a,int n,int key){

         IntI;

         For(i=1;i<=n;i++){

                   IF(a[i]==key)

                   ReturnI;

}

Return 0;

}

如何对于上面程序进行优化处理,这里有两个地方值得注意:(1)每次都要比较i和n的关系(2)每次都需要比较a[i]和key的关系;是否能够将这两个比较进行简化一下那,答案是可以的。

Int sequential_search(int *a,int n,int key){

         IntI;

         a[0]= key;

         i=n;

         while(a[i]!=key)i—

         returni;

}

这里给设置一个哨兵,a[0],如果中间有a[i]=key的话,返回当前i值,如果没有找到与key相等的项,则返回0的位置。这里讲循环都两次比较缩减为一次。但是当数据比较多时,效率应该是提高了很多。

顺序查找的时间复杂度,如果在第一个位置就查找到了,o(1);如果是在最后一个位置查找到则是o(n);如果没有查找到也是需要n+1次比较的,如果关键字被查找到的在每一位的概率是相同的话,那么平均查找概率当时o((n+1)/2).所以最终的时间复杂度还是o(n)

 

顺序查找的缺点是如果n很大的话,查找的效率是极低的。优点是算法简单,对于静态查询表没有任何要求,在一些小型整数数据查找时,是适用的。同时根据概率问题,可以再构造静态表时将容易查找到的数据放到查找的前面。

 

2.有序表查找

对于已经有序的结构,查找的效率是很高的。如猜商品的价格,给出一个区间,我们给出一个价格,别人回答值高还是低,这样很快就能找到。

这里同样也是通过折半查找,二分查找。其前提条件就是线性表中的记录是有序的(关键码),线性表必须用顺序存储。其基本思想是:在有序表中,取中间记录作为比较对象,如果给定值与中间记录的关键字相等,则成功;如果给定的值小于中间记录的关键字,则在中间记录和左半区继续查找;如果给定的值大于中间记录的关键字,则在中间记录的右半区继续查找。重复上面过程,直到查找成功,或所查找的区域无记录,查找失败。

 

Int binary_search(int *a;int n,int key){

         Intlow,high,mid;

         Low= 1;

         High= n;

         While(low<=high){//low<=high

                   Mid= (low+high)/2;

                   If(a[mid]<key){

         High = mid-1;

}else if (a[mid]>key)

{

         Low = mid+1;

}else

        Returnmid;

}

Return 0;

}

它的时间复杂度是o(logn),(利用具有n个结点的完全二叉树的深度是logn向下取整后+1)

如果是针对于静态查找表,这样的效果是足够好了,但是如果是数据频繁删除插入的情况的动态查找表的话,则将会是维护表的顺序有一定的工作量,不建议使用它。

 

3.插值查找

根据查找数据的大小,我们可以对于折半的这个地方进行优化,如数据很大,可以直接从后面;如果数据很小,可以从数据开始处理。这里是我们知道数据表的数值范围,排序顺序,同时知道查找的关键字的大概区域。有个动态调整自适应的变化,速度可以调节的快一些,或者调节的慢一些。

这里mid = (low+high)/2 = low +(high-low)/2,这一对于这里的1/2进行变化

如mid = low + ((key-a[low])/(a[high]-a[low]))*(high-low)

这里纯粹从公式上分析,如果key离a[low]很近的话,后面代替1/2的那部分的值就会很小;相反如果是key离a[high]很近,则这个值会很接近与1。也就是比原来变化的速度更快,能更快的接近于目标值。

这样就得到了另外一种有序表查找算法,插值查找法。插值查找法是根据要查找的关键字key与查找表中最大最小记录的关键字比较后查找方法,其核心在插值公式:

(Key-a[low])/(a[high]-a[low]).此外对于其他的插值公式,如果想要参考的话可以参见《数值分析》中讲解的各种插值形式。

这里从时间复杂度上仍然可以认为是o(logn)。但是如果表长比较长,且分布比较均匀,插值查找算法的平均性能要比二分法查找要好很多。

 

4.斐波那契查找

思想仍然是从二分法基础上而来,二分法是一分为2,插值查找是根据区间段的插值,而斐波那契查找则是利用黄金分割点,该数列的特性。

斐波那契数列:0 1 1 2 3 5 8 13 21 34 55 …

这里下标的改变使用该数列,其思想是:

(1)      当key=a[mid]时,查找成功;

(2)      当key<a[mid]时,新范围是第low个到第mid-1个,此时范围的个数为F[K-1]-1个;

(3)      当key>a[mid]时,新范围则是第mid+1个到第high个,此时范围个数为F[K-2]-1个;

注意这里开始补齐的a[n]到F[K]-1范围的数据,是防止比较过程中超出下标的比较,如果不补齐的话,将会出现数组越界比较的异常。F[K]为斐波那契数列

Int Fibonacci_search(int *a,int n,int key)

{

         Intlow,high,mid,I,k;

         Low= 1;

         High= n;

         K= 0;

         While(n>F(k)-1)k++;//计算n在斐波那契数列中的位置

         For(i=n;i<F[K]-1;i++)a[i]=a[n];//用a[n]补齐n到F[K]-1位置的数据

         While(low<=high){

                   Mid= low +F[K-1]+1;//

                   If(key<a[mid])

                   {

                            High= mid -1;

                            K= k-1;

}else if(key>a[mid])

{

         Low = mid+1;

         K = k -2;

}else{

         If(mid<n)return mid;//mid为查找的下标

         Else return n;//说明查找的位置是补全的数值,返回n

}

}

Return 0;

}

这里不熟悉斐波那契的可以研究一下。

其时间复杂度为o(logn).但是平均性能要比二分法要好。如果key=1,在左侧的话,效果就要低于二分法。

同时这里可以注意到二分法的(low+high)/2,插值法则是mid = low+((key-a[low])/(a[high]-a[low]))*(high-low),而斐波那契数列方法则是,mid=low+F[K-1]-1,在海量数据处理过程中,加减法要比乘除的效率要高一些。


从这算法,从顺序表查找,到二分法查找,再到插值查找,斐波那契查找;算法的发展方向,永远是算法的缺点和对于更好效率的追求。从简单原始的起点开始,在算法的性能上有着明显的可以提高的可能性,利用已有的数据结构和已有的知识点,将算法中某一块可以提高或者改变的地方,通过变形,或者采用转换,达到提高算法效率的可能。

对于已有的数据如果在输入时就具有了一定的特征,这一点是可以利用的,也就是说特殊的算法对于输入的数据有特定的要求,而且该特定格式的数据对于特定的算法是有一定耦合的,数据形式不一致,不同算法效率不同。即使是较好的算法,如果其需要的数据没有被特殊处理,其效果也并不是如期望的那样的。


再者说想排序算法:冒泡排序、快速选择、简单插入、希尔排序、堆排序、归并排序、快速排序等,思想是逐渐向数据结构靠拢、逐渐将大的问题分割成小的问题,分割组装的代价要比大问题的要小的多。

由整个数据前后遍历、到分区间遍历、或者是二分归并,我们可以看到算法优化的一条轨迹。可以当做设计的参考,如果能够给予我们一些启迪,或者能够有所顿悟,也是一种学习的反馈。学习在于悟。

同时对于输入的数据,我们也可以根据数据本身特点,或者是问题的简单的数据,我们顺序目标进行一些简单的操作,有时也能够从其中获得一些对于算法帮助的想法。


写到这里,想加上一句,不知道有关还是没有关系啊。索性加载最后:《易经》有三易,一是简易,二是变易,三是不易。简易:说明易经是简单的,我们可以从简单的入门,如果把数据结构和基本的算法粗度一遍,体会设计的奥妙,相信会有一些体会的。变易:任何事务都是变化的,我们只要搞清楚其变化的规律,那就了解了事务的本质,同时我们也要变,改变墨守陈规的状态。不易:正所谓一切道理的终极道理都是不变的;(也可以说不变的是变啊),把握住了这个之后,触类旁通啊。


最后最后再说一句话,孔子说研究《易经》,我们要不断把玩,要洁净精微。希望自己也能不断向这方面靠拢啊。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值