数据结构与算法23-查找

查找概念

查找表(Search  Table)是由同一类型的数据元素(或记录)构成的集合。

关键字(Key)是数据元素中某个数据的值。又称为键值。可以标识一个记录的某个数据项(字段),我们称为关键码。

若此关键字可以唯一地标识一个记录,则称此关键字为主关键字(Primary  Key)。这说明对不同的记录,其主关键字均不相同。主关键字所在的数据项称为主关键码。

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

查找就是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)

静态查找表(Static Search Table):只作查找操作的查找表。

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

2.    检索某个“特定的”数据元素和各种属性。

就是在在已经有的数据中找到我们需要的。但是往往有的时候我们是想在查找结果后去对结果进行一些操作。

动态查找表(Dynamic    Search  Table):在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。显然动态查找表的操作就两个:

1.    查找时插入数据元素

2.    查找时删除数据元素

为了提高查找的效率,我们需要专门为查找操作设置数据结构,这种面向查找操作的数据结构称为查找结构。

从逻辑上来说,查找所基于的数据结构是集合,集合中的记录之间没有本质关系。可是要想获得较高的查找性能,我们就不能不改变数据元素之间的关系,在存储时(内存里)可以将查找集合组织成表、树等结构。

顺序表查找

比如对散落的一堆书进行查找,散落的图书可以理解为一个集合,而将它们排列整齐,就如同是将此集合构造成一个线性表。我们要针对这一线性表进行查找操作,因此它就是静态查找表

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

顺序表查找算法

/*顺序查找,a为数组,n为要查找的数组长度,key为要查找的关键字*/

int  Sequential_Search(int  *a,int  n,int   key)

{

      int   i;

      for(i=1;i<=n;i++)

      {

              if(a[i]==key)

                 return i;

       }

       return   0;

}

顺序表查找优化

int  Sequential_Search2(int *a,int  n,int  key)

{

        int i;

       a[0]=key; //设置a[0]为关键字值,我们称之为“哨兵”

       i=n;    //循环从数组尾部开始

       while(a[i]!=key)

        {

                  i--;

         }

         return i;   //返回0则说明查找失败。

}

如果有key则返回i值,否则一定在最终a[0]处等于key,返回0。说明a[1]~a[n]中没有关键字key,查找失败。

这种在查找方向的尽头放置“哨兵”免去了在查找过程中每一次比较后都要判断查找位置是否越界的小技巧,看假与原先差别不大,但在总数据较多时,效率提高很大,是非常好的编码技巧。

有序表查找

对于一个线性表有序时,对于查找总是很有帮助

折半查找

折半查找(Binary   Search)技术,又称为二分查找。它的前提是线性表中的记录必须是关键码有序(通常从小到大有序),线性表必须采用顺序存储。取中间记录作为比较对象,若给定值与中间记录的关键字相等,查找成功,若小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直至查找成功,或所有查找区域无记录,查找失败为止。

{0,1,16,24,35,47,59,62,73,88,99},除0下标外共10个数字

对它进行查找是否存在62这个数。我们来看折半查找的算法是如何工作的

int   Binary_Search(in  *a,int  n,int key)

{

     int  low,high,mid;

     low=1;

     hight=n;

     while(low<=high)

      {

              mid=(low+high)/2

              if(key<a[mid])

                 high=mid-1;

              else if(key>a[mid])

                 low=mid+1;

               else

                    return   mid;

       }

        return  0;

}

插值查找

我们将折半查找的计算略微变成一下如下:

改进为下面的计算方案:

将1/2改成了

实际上就是将查找代码中的第8行改为

插值查找(Interpolation   Search),是根据要查找的关键字key与查找表中最大最小记录的关键字比较后的查找方法,其核心就在于插值的计算公式

斐波那契查找

它是用黄金分割原理来实现的。为了能够介绍清楚这个查找算法,我们先需要有一个斐婆那契数列的数组。如图

我们来看一下代码如下:

int  Fibonacci_Search(int  *a ,int   n,int key)

{

        int  low,higth,mid,i,k;

       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;    //最高下标调整到分隔下标的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;    //mid>n说明是补全数值,返回n

                   }

          }

}

模拟一下

1.    程序开始运行,参数a={0,1,16,24,35,47,59,62,73,88,99}   n=10,要查找的关键字key=59。注意此时我们已经有了事先计算好的全局变量数组F的具体数据,它是斐波那契数列F={0,1,1,2,3,5,8,13,21,…}

2.    第6~8行是计算当前的n处于斐波那契数列的位置。现在n=10,F[6]<n<F[7],所以计算得出k=7

3.    第9~10行,由于k=7,计算时,是以F[7]=13为基础,而a中最大的仅是a[10],后面的a[11],a[12]均未赋值,这不能构成有序数列,因此将它们赋值为最大数组值,所以此时,a[11]=a[12]=a[10]=99

4.    11~31行查找正式开始

5.    第13行,mid=1+F[7-1]-1=8,也就是说,我们第一个要对比的数值是从下标为8开始的。

6.    由于此时key=59而a[8]=73,因此执行第16~17行,得到high=7,k=6

7.    再次循环,mid=1+F[6-1]-1=5,此时a[5]=47<key,因此执行21~22行,得到low=6,k=6-2=4。注意此时k下调2个单位。

8.    再次循环,mid=6+F[4-1]-1=7。此时a[7]=62>key,因此执行16~17行,得到high=6,k=4-1=3;

9.    再次循环,mid=6+F[3-1]-1=6。此时a[6]=59=key。因此执行执行第26~27行,得到返回值6。程序运行结构。

如果key=99,此时查找循环第一次时,mid=8与上例是相同的,第二次循环时,mid=11,如果a[11]没有值就会使得与key的比较失败,为了避免这样的情况出现,第9~10行的代码就起到这样的作用。

斐波那契查找算法的核心在于:

1.    当key=a[mid]时,查找就成功

2.    当key<a[mid]时,新范围是第low个到第mid-1个,此时范围个数为F[k-1]-1个

3.    当key>a[mid]时,新范围是第mid+1个到第hight个,此时范围个数为F[k-2]-1个

折半查找是进行的加法与除法运算(mid=(low+high)/2),插值查找进行的是复杂的四则运算(mid=low+(high-low)*(key-a[low])/(a[high]-a[low])),而斐波那契查找只是最简单的加减法运算。对于海量数据会有效率上的影响

本质上有序表的查找本质上是分隔点选择不同,各有优劣。

猜你喜欢

转载自blog.csdn.net/lhr434348820/article/details/82901133