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

原创 2015年11月20日 17:48:04

查找:

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

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

如果此关键字可以唯一的表示一个记录,则称此关键字为主关键字(类此数据库中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,在海量数据处理过程中,加减法要比乘除的效率要高一些。


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

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


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

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

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


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


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

 

搜索算法集锦

搜索有以下几种算法: 枚举算法: 也即列举问题的所有状态从而寻找符合问题的解的方法。 适合用于状态较少,比较简单的问题上。 广度...
  • llingy
  • llingy
  • 2010年06月16日 21:38
  • 30741

搜索有以下几种算法:

枚举算法: 也即列举问题的所有状态从而寻找符合问题的解的方法。 适合用于状态较少,比较简单的问题上。 广度优先搜索: 从初始点开始,根据规则展开第一层节点,并检查目标节点是否在这些节点...

两种简单的搜索算法

搜索算法是图论中常用到的算法,这里介绍两种常用的算法。这两种算法都是是以队列为基础的。 最简单的搜索方法:迷宫搜索,这种搜索方法是从当前位置出发,向四周辐射,直到搜索到目标位置,这种搜索方法的特性是...

一些简单的算法

入门算法冒泡排序时间复杂度o(n²),空间复杂度o(1),最差的排序。 快速排序,快速排序(Quicksort)是对冒泡排序的一种改进,排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经...

简单排序算法

冒泡排序: import java.util.Arrays; public class Bubbling { /** * @param args * 冒泡排序: ...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

简单的猜拳游戏 却有不简单的算法

import java.util.Scanner; class Zhangsan { void showTitle() { System.out.println(" ...

算法最简单之希尔算法

公共函数: less: public static boolean less(Comparable v , Comparable w){ return v.compareTo(w) < 0; }...

一些简单的排序、查找算法

1、插入排序         算法思想简单描述:在要排序的一组数中,假设前面(n-1)[n>=2] 个数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。如此反复循环,...

查找算法系列之简单查找:顺序查找、二分查找、分块查找

近期总结了各大排序算法的原理 ,并对其进行了实现,想着一并把查找算法总结了,今天就着手开始总结查找算法。 废话不多说,这篇文章从最简单的查找算法开始讲起,之后会补充复杂的二叉搜索树查找(BST)和B树...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:查找算法(4个简单的算法)
举报原因:
原因补充:

(最多只允许输入30个字)