24. 查找算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dugudaibo/article/details/79432214

1. 相关概念

1.1 静态查找和动态查找

  静态查找:数据集合稳定,不需要添加,删除元素的查找操作。

  动态查找:数据集合在查找的过程中需要同时添加或删除元素的查找操作。

1.2 查找结构

  对于静态查找来说,我们不妨可以用线性表结构组织数据,这样可以使用顺序查找算法,如果我们再对关键字进行排序,则可以使用折半查找算法或斐波那契查找算法等来提高查找的效率。

  对于动态查找来说,我们则可以考虑使用二叉排序树的查找技术,另外我们还可以使用散列表结构来解决一些查找问题,这些技术我们都将在这部分教程里边介绍给大家。

2. 顺序查找

2.1 思路

  顺序查找又叫线性查找,是最基本的查找技术,它的查找过程是:从第一个(或者最后一个)记录开始,逐个进行记录的关键字和给定值进行比较,若某个记录的关键字和给定值相等,则查找成功。如果查找了所有的记录仍然找不到与给定值相等的关键字,则查找不成功。

2.2 代码

  首先是一种最直觉的实现方法

// 顺序查找,a为要查找的数组,n为要查找的数组的长度,key为要查找的关键字
int Sq_Search(int *a, int n, int key)       //其中的 *a 的含义是数组的头指针,传入头指针相当于传入了这个数组
{
    int i;
    for( i=1; i <= n; i++ )
    {
        if( a[i] == key )
        {
            return i;
        }
    }
    return 0;
}

但是上面这段代码的复杂度是 2n ,那有每一种复杂度为 n 的查找代码呢?有的,如下优化方案

// 顺序查找优化方案,a为要查找的数组,n为要查找的数组的长度,key为要查找的关键字
int Sq_Search(int *a, int n, int key)
{
    int i = n;
    a[0] = key
    while( a[i] != key )
    {
        i--;
    }

    return i;
}

这个算法虽然复杂度只有 n ,但是同样存在着一个问题,就是它对于里面具有多个相同的数的情况是没有考虑在内的。

2.3 相关思考题

  假设以下有一个结构体存放的是学生的记录,每条记录包括:学号、姓名、成绩,请编写一个程序,要求输出1024编号同学的具体信息。

2.3.1 代码

#include <stdio.h>

typedef struct student
{
    int id;
    char name[10];
    float score;
}Student;

float search(Student stu[], int n, int key)
{
    int i;

    for( i=0; i < n; i++ )
    {
        if( stu[i].id == key )
        {
            return i.score;
        }
    }

    return -1;
}

int main()
{
    Student stu[4] = {
        {1024, "小甲鱼", 100}, 
        {1026, "秋舞斜阳", 60}, 
        {1028, "黑夜", 100}, 
        {1030, "迷途", 60}
        };

    float score;

    score = search(stu, 4, 1024);
    printf("1024号童鞋的成绩是:%f", score);

    return 0;
}

3. 折半查找

  如果从文件中读取的数据记录的关键字是有序排列的,则可以用一种效率比较高的查找方法来查找文件的记录,这就是折半查找法,又称为二分法搜索。

3.1 基本思想

  减小查找序列的长度,分而治之地进行关键字的查找。先确定待查找记录的所在范围,然后逐渐缩小这个范围,直到找到该记录或查找失败(查无该记录)为止。

  例如有序列:1 1 2 3 5 8 13 21 34 55 89(该序列包含 11 个元素,而且关键字单调递增。),现要求查找关键字 key 为 55 的记录。我们可以设指针 low 和 high 分别指向关键字序列的上界和下界,指针 mid 指向序列的中间位置,即 mid = (low+high)/2。如下图所示

这里写图片描述

首先将 mid 所指向的元素与 key 进行比较,因为我们这里 key = 55,大于 8,这就说明待查找的关键字一定位于 mid 和 high 之间。于是我们执行 low = mid+1; mid = (low+high)/2;如下图所示

这里写图片描述

然后再将 mid 所指的 34 与 key 进行比较,仍然 mid < key,所以继续执行 low = mid+1; mid = (low+high)/2;如下图所示


这里写图片描述

接下来仍然将 mid 所指的元素与 key 进行比较,结果相等,查找成功。返回 mid 的指针值,程序结束。假设我们要查找的关键字 key = 88,那么上述的查找还要继续进行下去 low = mid+1; mid = (low+high)/2;如下图所示


这里写图片描述

3.2 迭代实现

#include <stdio.h>

int bin_search( int str[], int n, int key )
{
        int low, high, mid;

        low = 0;
        high = n-1;

        while( low <= high )
        {
                mid = (low+high)/2;
                if( str[mid] == key )
                {
                        return mid;                // 查找成功
                }
                if( str[mid] < key )
                {
                        low = mid + 1;        // 在后半序列中查找
                }
                if( str[mid] > key )
                {
                        high = mid - 1;        // 在前半序列中查找
                }
        }

        return -1;                                // 查找失败
}

int main()
{
        int str[11] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89};
        int n, addr;

        printf("请输入待查找的关键字: ");
        scanf("%d", &n);

        addr = bin_search(str, 11, n);
        if( -1 != addr )
        {
                printf("查找成功, 关键字 %d 所在的位置是: %d\n", n, addr);
        }
        else
        {
                printf("查找失败!\n");
        }

        return 0;
}

3.3 递归实现

#include<stdio.h>
#define SIZE 10
typedef int ElemType;
int refind(ElemType *data,int begin,int end,ElemType num);
int main(void){
            ElemType data[SIZE]={10,20,30,40,50,60,70,80,90,100};
            ElemType num;
            for(int i = 0;i<SIZE;i++)
                    printf("%d ",data[i]);
            printf("\n请输入要查找的数据:\n");
            scanf("%d",&num);
            int flag = refind(data,0,SIZE,num);
            printf("位置为:%d\n",flag);
            return 0;
}
/
//递归
int refind(ElemType *data,int begin,int end,ElemType num)
{
            if(begin > end)
            {
                      printf("没找到\n");
                      return -1;
             }

             int mid = (begin+end)/2;

             if(data[mid] == num)
            {
                      return mid;
             }else if(data[mid] <= num)
                      return refind(data,mid+1,end,num);
             else
                      return refind(data,begin,mid-1,num);
}

4. 插值查找(按比例查找)

  在上面的折半查找法中,每次都是对折之后再查找,但是实际上是可以不进行对折查找。比如说,我们在英语字典中查找某一个英语单词,首先会翻到这个单词的首字母的位置再进行查找,这种查找方式就叫做按比例查找。

4.1 基本思想

  它的基本思想就是,根据关键之在数组中的位置来确定 mid 的位置。

4.2 代码

#include <stdio.h>

int bin_search( int str[], 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( str[mid] == key )
        {
            return mid;              
        }
        if( str[mid] < key )
        {
            low = mid + 1;       
        }
        if( str[mid] > key )
        {
            high = mid - 1;       
        }
    }

    return -1;                      
}

int main()
{
    int str[11] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89};
    int n, addr;

    printf("请输入待查找的关键字: ");
    scanf("%d", &n);

    addr = bin_search(str, 11, n);
    if( -1 != addr )
    {
        printf("查找成功,可喜可贺,可口可乐! 关键字 %d 所在的位置是: %d\n", n, addr);
    }
    else
    {
        printf("查找失败!\n");
    }

    return 0;
}

5. 斐波那契查找(黄金分割法查找)

  我们知道黄金分割比是0.618,但是这个比例实际上和斐波那契数列也是有关系的,比如如下的斐波那契数列

1,1,2,3,5,8,13,21,34,55,89

在这个数列中可以看到,随着斐波那契数列数值的逐渐增大,两个数值之间的比例越来越接近与 0.618,这也就是斐波那契数列与黄金分割之间的关系。

5.1 基本思想

  斐波那契查找的主要思想如下图所示

在这里面我们将数组分为前后两个部分,假设整个数组中元素的个数是 F[k]+1,那么将它分为前后两个部分,前面是 F[k-1],后面是 F[k+1],其中的 k 是指斐波那契数列中的位置,但是在实现的过程中数组往往是从 0 开始的,所以实际上是下面的这种形式

5.2 代码

#include <stdio.h>
#define MAXSIZE 20

void fibonacci(int *f)   //使用递推关系生成斐波那契数列
{
    int i;

    f[0] = 1;
    f[1] = 1;

    for(i=2; i < MAXSIZE; ++i)
    {
        f[i] = f[i-2] + f[i-1];

    }
}

// *a 是待查找数组的头指针,key 待查找的数字,n 是待查找数组中元素的个数
int fibonacci_search(int *a,int key,int n)
{
    int low = 0;
    int high = n - 1;
    int mid = 0;
    int k = 0;
    int F[MAXSIZE];
    int i;

    fibonacci(F);

    //找到与数组长度相对应的数列中的元素
    while( n > F[k]-1 ) 
    {
        ++k;
    }

    //如果数组的长度小于对应的斐波那契数列中元素的值,那么将他用最大值拓展
    for( i=n; i < F[k]-1; ++i)
    {
        a[i] = a[high];
    }

    while( low <= high )
    {
        mid = low + F[k-1] - 1;

        if( a[mid] > key )
        {
            high = mid - 1;
            k = k - 1;
        }
        else if( a[mid] < key )
        {
            low = mid + 1;
            k = k - 2;
        }
        else
        {
            if( mid <= high ) 
            {
                return mid;
            }
            else
            {
                return high;
            }
        }
    }

    return -1;
}

int main()
{

    int a[MAXSIZE] = {1, 5, 15, 22, 25, 31, 39, 42, 47, 49, 59, 68, 88};
    int key;
    int pos;

    printf("请输入要查找的数字:");
    scanf("%d", &key);

    pos = fibonacci_search(a, key, 13);

    if( pos != -1 )
    {
        printf("\n查找成功,关键字 %d 所在的位置是: %d\n\n", key, pos);
    }
    else
    {
        printf("\未在数组中找到元素:%d\n\n", key);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/dugudaibo/article/details/79432214
今日推荐