无序数组的二分查找法

前言

    我是一个只学了两个月C语言的小白,刚开始学指针。

    老师上课讲“二分查找”的时候强调只能用于有序数组,课后我本着巩固知识的态度自己写了一遍二分查找的代码,并思考能否对无序数组进行排序操作后,用二分查找法找到目标数,并找到它在无序数组中的下标。

    但这样就违背了二分查找法的初衷。二分查找法本来是为了在处理一个巨大数组时减少运算量,提高效率,比如在40多亿个数据中找数,运用二分查找只需要三十几次就够了,这比逐一排查快得多的多。但如果是一串很长的无序数组,仅排序的运算量就比逐一排查要多,效率更低。

    不过还是本着用于练习的想法,我决定写一写。同时对“有重复数字”的情况进行了处理。

    大家随便看看,图一乐就行。

二分查找法

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

    二分查找的基本思想是将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x,如果x>a[n/2],则只要在数组a的右半部搜索x.

    大致思路如下:

#include <stdio.h>

int main()
{
 int arr[] = {1,2,3,4,5,6,7,8,9,10};

 int left = 0;

 int right = sizeof(arr)/sizeof(arr[0])-1;

 int key = 7;//要找的数字 

 int mid = 0;//记录中间元素的下标 

 int find = 0;

 while(left<=right)
 {
     mid = (left+right)/2;
     if(arr[mid]>key)
     {
         right = mid-1;
     }
     else if(arr[mid] < key)
     {
         left = mid+1;
     }
     else
     {
     find = 1;
     break;
     }
 }

 if(1 == find )
 {
     printf("找到了,下标是%d\n", mid);
 }
 
 else
 {
     printf("找不到\n");
 }

 return 0;
}

这里有一点要注意:

mid = (left + right) / 2

这一行代码,mid是int类型变量,如果left和right数据过大,可能导致溢出,而运行出错。因此可做如下改进:

mid = left + (right - left) / 2

这两种运算结果相同,但第二种在运算过程中的产生的最大值会比第一种小得多,不容易出错。

无序数组的二分查找法

    我在写代码过程中遇到的困难:如何在找到目标数字后,再找到它在原来的无序数组中的位置(下标)。由于我刚开始接触指针,所以无法用指针解决。经过长时间的思考,我认为核心在于将a[](输入的数组)中每个元素与其相应的下标绑定在一起。所以,我想到了二维数组。

    大致思路:

    1.输入a[n]数组(方便起见,用预定义将n赋值为10)

    2.将a[]的元素与对应下标绑定,放入二维数组b[n][2]中

    3.对b[i][1]进行排序(这里用的是冒泡法排序)

    4.对排序后的b[i][1]进行二分查找,从而确定i(mid),得到原数组下标b[i][0]

#include<stdio.h>

#define n 10

int main()
{
    //1.输入a[]数组
    int a[n];

    printf("请输入:\n");

    for(int i = 0; i < n; i++)
    {
        scanf("%d",&a[i]);
    }

    //2.将a[]的下标及对应的元素绑定放入b[n][2]中
    int b[n][2];

    for(int i = 0; i < n; i++)
    {
        b[i][0] = i;

        b[i][1] = a[i];
    }

    //3.对b[i][1]进行排序。需要交换时,记得两个数都要交换
    for(int j = 1; j < n; j++)
    {
        for(int i = 0; i < n-j; i++)
        {
            if(b[i][1] > b[i+1][1])
            {
                int t;
                
                //交换第一列(a[])下标
                t = b[i][0];
                b[i][0] = b[i+1][0];
                b[i+1][0] = t;
                //交换第二列(元素)
                t = b[i][1];
                b[i][1] = b[i+1][1];
                b[i+1][1] = t;
            }
        }
    }

    //输出排序后的数组(这一步不需要)
    printf("排序后的数组:\n");
    for(int i=0;i<n;i++)
    {
        printf("%d ",b[i][1]);
    }
    printf("\n");

    //4.对b[i][1]进行二分查找
    int left = 0;

    int right = n - 1;

    int mid = 0;

    int find = 0;//0表示没找到

    int key;//要找的数

    printf("请输入要找的数:\n");

    scanf("%d",&key);

    while(left <= right)
    {
        mid = left + (right - left) / 2;

        if(b[mid][1] > key)
        {
            right = mid - 1;
        }
        else if(b[mid][1] < key)
        {
            left = mid + 1;
        }
        else
        {
            printf("找到了,a[%d]是要找的数。", b[mid][0]);

            find = 1;

            break;
        }
    }

    if(find == 0)
    {
        printf("未找到要找的数。");
    }

    return 0;
}

运行结果截图:

有重复数字的情况

    这里使用正常的二分查找。由于是有序数组,所以重复数字必然是连在一起的。我们需要从找到的一个数开始,往左右依次找其他数。

    

#include <stdio.h>

int main()
{
    int arr[] = {1,2,3,6,6,6,7,8,9,10};

    int left = 0;

    int right = sizeof(arr)/sizeof(arr[0])-1;

    int key = 6;//要找的数字 

    int mid = 0;//记录中间元素的下标 

    int find = 0;

    while(left<=right)
    {
       mid = (left+right)/2;

       if(arr[mid]>key)
       {
           right = mid-1;
       }
       else if(arr[mid] < key)
       {
           left = mid+1;
       }
       else
       {
           find = 1;

           break;
       }
    }

    if(1 == find )//至少找到一个数了
    {
        int MID = mid + 1;//MID是找到的第一个数右侧第一个数,方便向两侧查找

        printf("找到了,下标是");
        
        while(arr[mid] == key)//向左查找
        {
            printf("%d ", mid);

            mid--;
        }

        while(arr[MID] == key)//向右查找
        {
            printf("%d ", MID);

            MID++;
        }
    }   
    else
    {
        printf("找不到\n");
    }

    return 0;
}

运行结果截图:

这里下标没按顺序输出,因为是从中间向两边查找的。如果要按顺序的话,可以用数组存一下然后再按顺序输出。也可以先向左查找,找到最左边的之后,再向右查,依次输出。

下面用第二种演示一下

    if(1 == find )//至少找到一个数了
    {
        for(;arr[mid] == key; mid--);

        mid++;
        
        printf("找到了,下标是");

        for(;arr[mid] == key; mid++)
        {
            printf("%d ", mid);
        }

    }

运行结果:

结束!

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值