数据结构(六)查找---有序表查找(三种查找方式:折半,插值,斐波拉契查找)

前提

有序表查找要求我们的数据是有序的,是排序好的,我们只需要进行查找即可

我们下面将介绍折半查找(二分查找),插值查找,斐波那契查找

一:折半查找

(一)定义

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。

(二)查找过程

首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

(三)代码实现

int Binary_Search(int *a, int n, int key)
{
    int low, high, mid;
    low = 0;
    high = n - 1;
    while (low<=high)
    {
        mid = (low + high) / 2;
        if (a[mid] < key)
            low = mid + 1;
        else if (a[mid]>key)
            high = mid - 1;
        else
            return mid;
    }
    return -1;
}

int main()
{
    int a[10] = { 1, 6, 12, 21, 30, 31, 32, 42, 49, 52 };
    int index;
    index=Binary_Search(a, 10, 49);
    if (index != -1)
        printf("find key in %d\n", index);
    else
        printf("not find key\n");
    system("pause");
    return 0;
}

(四)性能分析

其时间复杂度是O(logn),不过由于折半查找的前提是需要有序表顺序存储,对于静态查找表,一次排序后不再变化,这样的算法是比较好的。但是对于需要频繁插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,不建议使用。

二:插值查找(按比例查找法)

(一)算法分析:

对于前面的折半查找,为啥一定要折一般,而不是1/4或者其他?
比如:我们查字典Apple,我们会先从中间查找,还是有一定目的的向前找。或者在0-1000个数之间有200个元素从小到大均匀分布排序,我们若是需要查找到15,我们会去中间查找500,还是直接去前面开始查找。
以上都说明我们上面的折半查找可以再进行改进

首先我们对折半公式进行改写:

折半查找这种查找方式,不是自适应的(也就是说是傻瓜式的),其前面的查找系数(比例参数)始终是1/2

通过类比,我们可以将查找的点改进为如下:

也就是将上述的比例参数1/2改进为自适应的,根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。

(二)基本思想:

基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,插值查找也属于有序查找。

(三)代码实现:

int Insert_Search(int *a, int n, int key)
{
    int low, high, mid;
    low = 0;
    high = n - 1;
    while (low <= high)
    {
        mid = low + (key - a[low]) / (a[high] - a[low])*(high - low);
        if (a[mid] < key)
            low = mid + 1;
        else if (a[mid]>key)
            high = mid - 1;
        else
            return mid;
    }
    return -1;
}
实现方法和折半一样,只是修改了mid

(四)性能分析:

而插值查找则比较灵活,并不是简单的从中间进行的,它是根据我们需要查询的值的渐进进行搜索的.

插值查找的不同点在于每一次并不是从中间切分,而是根据离所求值的距离进行搜索的.
对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
时间复杂度:平均情况O(loglog(n)),最坏O(log(n))

三:斐波那契查找(仅使用加法减法实现二分查找)

除了插值查找之外,我们再介绍一种有序查找,可以对折半进行优化,那就是斐波那契查找,利用了黄金分割原理来实现的
斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,即n=F(k)-1;

(一)斐波那契数列

越向后,每两个数之间相除越接近黄金比例

(二)斐波拉契查找实现

1.首先我们要创建一个斐波拉契数列

2.获取我们的数组大小n(我们不考虑0下标,因为我们的斐波那契数列从0开始,无法构成黄金比例),在斐波拉契数列中的位置

例如上面的查找数组大小为n=10,F[6]<n<F[7],所以得出其位置为k=7

3.因为我们的k=7,F[7]=13,而a数组最大为10,我们需要对其按照F[7]=13补齐数组,使其长度为F[7]-1=12,将a[11]=a[10],a[12]=a[10]

4.开始正式查找,按照上图mid=low+F[k-1]-1;

(三)思考:n=F(k)-1, 表中记录的个数为某个斐波那契数小1。这是为什么呢?

推文:斐波那契查找原理详解与实现

是为了格式上的统一,以方便递归或者循环程序的编写。表中的数据是F(k)-1个,使用mid值进行分割又用掉一个,那么剩下F(k)-2个。正好分给两个子序列,每个子序列的个数分别是F(k-1)-1与F(k-2)-1个,格式上与之前是统一的。不然的话,每个子序列的元素个数有可能是F(k-1),F(k-1)-1,F(k-2),F(k-2)-1个,写程序会非常麻烦。

(四)算法实现

void Fibonacci(int **Fb, int n)
{
    int f1, f2,ft;
    int count=3;
    f1 = 1; f2 = 1;

    while (f2<n)
    {
        ft = f1 + f2;
        f1 = f2;
        f2 = ft;
        count++;
    }
    (*Fb) = (int *)malloc(count*sizeof(int));
    (*Fb)[0] = 0;
    (*Fb)[1] = 1;
    for (f1 = 2; f1 <= count - 1; f1++)
        (*Fb)[f1] = (*Fb)[f1 - 1] + (*Fb)[f1 - 2];
}

创建斐波那契数列

int Fibonacci_Search(int *a, int n, int key)
{
    int low, high, mid, i, j,k;
    int *Fb,*temp;
    //根据实际情况来初始化
    low = 1;
    high = n;

    Fibonacci(&Fb, n);
    k = 0;

    while (n > Fb[k])    //计算n位于斐波那契数列位置
        k++;

    temp = (int *)malloc((Fb[k] - 1 + 1)*sizeof(int));//后面加1是因为还有个下标0空间
    memcpy(temp, a, n*sizeof(n + 1));

    for (i = n + 1; i <= Fb[k] - 1; i++)    //我们要查找的是数组1到F[k]-1,而实际数组长要包括一个下标0空间
        temp[i] = a[n];

    while (low<=high)
    {
        mid = low + Fb[k - 1] - 1;
        if (key == temp[mid])
        {
            //分情况,是前半段,正常输出,后半段判断是不是在我们补充的数组里面,这时我们返回原数组最后一个下标
            if (mid <= n)
                return mid;
            else
                return n;
        }
        else if (key>temp[mid])
        {
            low = mid + 1;
            k = k - 2;
        }
        else
        {
            high = mid - 1;
            k = k - 1;
        }
    }
    return 0;    //0是未找到,0下标使无意义的
}
int main()
{
    int a[11] = { 0,1,16,24,35,47,59,62,73,88,99};
    int index;
    index = Fibonacci_Search(a, 10, 73);
    if (index != -1)
        printf("find key in %d\n", index);
    else
        printf("not find key\n");
    system("pause");
    return 0;
}

(五)性能分析

其时间复杂度也是O(logn),就平均性能来说,斐波那契查找由于折半查找。但是若是最坏情况,比如key=1,始终处于左侧长半区查找,则效率低于折半查找。

四:总结

原文地址:https://www.cnblogs.com/ssyfj/p/9499162.html

时间: 2025-01-15 19:27:52

数据结构(六)查找---有序表查找(三种查找方式:折半,插值,斐波拉契查找)的相关文章

(java)有序表查找——折半查找,插值查找,斐波那契查找

有序表查找 /* 主函数 */ public class OrderTableSearch { public static void main(String[] args) { int [] a= {0,1,16,24,35,47,59,62,73,88,99}; System.out.println(FibonacciSearch(a, 10, 88)); System.out.println(InsertKeySearch(a, 10, 88)); System.out.println(Bi

看数据结构写代码(53) 静态查找表(线性查找,二分查找,斐波那契查找,插值查找)

查找定义:根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录). 查找表分类:静态查找表和动态查找表. 静态查找表:只查找,而不进行插入,删除. 动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素. 静态表的 查找 大致 四种 算法: 线性查找,二分查找,斐波那契查找和插值查找. 其中 在线性查找之前,对表 无要求.对于 其余三种 需要 在查找之前 排序.插值查找 除了 需要 排序,还需要 均匀分布. 下面 给出代码: 线性查

数据结构之---C语言实现斐波那契查找

斐波那契查找的核心思想是: 1)当key=a[mid]时,查找成功: 2)当key<a[mid]时,新的查找范围是第low个到第mid-1个,此时范围个数为F[k-1] - 1个,即数组左边的长度,所以要在[low, F[k - 1] - 1]范围内查找: 3)当key>a[mid]时,新的查找范围是第mid+1个到第high个,此时范围个数为F[k-2] - 1个,即数组右边的长度,所以要在[F[k - 2] - 1]范围内查找. 代码: //斐波那契查找 //杨鑫 #include <

斐波那契查找算法 -数据结构

这个是我本人写的斐波那契查找算法,和网上的其他思路略有不同,特贴出来: 1)没有预先存储斐波那契的数组,整个的搜索数据长度可以变动: 2)mid索引的变动是动态变化的,根据斐波那契的回退方法: #include<stdlib.h>#include<stdio.h> #define INF 32767#define SIZE 92int Feibinaci(int str[],int n,int key){ int F0 = 1; int F1 = 1; int temp; int

【算法】先生,您点的查找套餐到了(二分、插入和斐波那契查找)

参考资料 <算法(java)>                           — — Robert Sedgewick, Kevin Wayne <数据结构>                                  — — 严蔚敏 Interpolation Search[插值查找]     — —  维基百科 Fibonacci Search[斐波那契查找]   — —  GeeksforGeeks 根据输入的一个关键字(Key),  在一个有序数组内查找与该关键

二分查找和斐波那契查找

二分查找 说明:查找的数组或列表必须是有序的,若无序,先进行排序 复杂度:时间复杂度 O(log2n),空间复杂度O(n) C++源码(递归和非递归两个版本) #include <iostream> using namespace std; int a[] = { 1, 2, 3, 4, 5, 6, 8 }; int BinarySearch1(int l, int r, int value) { int mid = (l + r) / 2; if (l == r && a[l

斐波那契查找原理详解与实现

最近看见一个要求仅使用加法减法实现二分查找的题目,百度了一下,原来要用到一个叫做斐波那契查找的的算法.查百度,是这样说的: 斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的.他要求开始表中记录的个数为某个斐波那契数小1,即n=F(k)-1;  开始将k值与第F(k-1)位置的记录进行比较(及mid=low+F(k-1)-1),比较结果也分为三种  1)相等,mid位置的元素即为所求  2)>   ,low=mid+1,k-=2;说明:low=mid+1说明待查找的元素在

斐波那契查找与黄金分割

黄金分割:指事物各部分间一定的数学比例关系,即将整体一分为二,较大部分与较小部分之比等于整体与较大部分之比,其比值约为1:0.618或1.618:1: 0.618被公认为最具有审美意义的比例数字,不仅体现在诸如绘画.雕塑.音乐.建筑等艺术领域,而且在管理.工程设计等方面也有着不可忽视的作用,因此被称为黄金分割: 斐波那契数列:1,1,2,3,5,8,13,21,34,55,89......(从第三个数开始,后边每一个数都是前两个数的和),然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会

斐波那契查找法

void Fibonacci(int *f) { f[0] = 1; f[1] = 1; for (int i = 2; i < MAXSIZE; i++) { f[i] = f[i - 1] + f[i - 2]; } } int Fibonacci_Search(int *a, int n, int key) { int low, high, mid; low = 1; high = n - 1; int k = 0; int F[MAXSIZE]; Fibonacci(F); //问题一: