C语言基础之二分查找知识最全汇总

 更新时间:2021年04月29日 14:18:14   作者:峡谷金城武  
这篇文章主要介绍了C语言基础之二分查找知识最全汇总,文中有非常详细的二分查找基础知识详解,对正在学习C语言基础的小伙伴们有非常好的帮助,需要的朋友可以参考下

一、前言

在自学二分查找的过程中我想到了一些变化问题,有的自己就慢慢理解了,有的在网上找到了答案,奈何没有找到想要的总结归纳。我就斗胆自己写了一篇,号称史上最全。希望和我一样的伙伴可以少走一点弯路。

二分查找凭借其低时间复杂度O(log(n))成为了各个蒟蒻的入门知识,但是其衍生出的各种题目相较原题目而言就没有那么容易求解,以下借用c语言实现二分查找算法及其衍生。二分查找仅适用于事先已经排好序的顺序表。其基本思路就是每次取中间数,如果中间数大于所求数就向上查找,反之向下。

二、原始二分查找

1.在有序数组中寻找一个数所在下标。

int main(){
    int arr[] = {1,2,3,4,5,6,7,8,9};
    int left,right,fin;
    fin = 3;
    left = 0;right = sizeof(arr)/sizeof(arr[0])-1;
 
    while(left <= right) {
        int mid;
        mid = (left+right) / 2;
        if (arr[mid] == fin) {
            printf("找到了:%d\n", mid);
            return 0;
        }
        if (mid < fin)
            left = mid+1;
        else if (mid > fin)
            right = mid-1;
 
    }
    printf("没找到");
    return 0;
}

对于这种基本的排序问题,我们通过判断mid变量与key的大小对有序数组进行二分。

当查找过程中,会出现key值下标坐落在left或right上时的情况,

以下用left举例

当left = key时,一个显然的结论,此时arr[mid]无法等于key。其所作工作为减小right,进一步缩进范围,直到right = left或right = left+1,此时范围缩小到最小,mid = left。输出mid的值即为所求下标。

我们当然可以在left和right = key时直接输出key的下标,但是这样会造成多次比较。

三、二分查找的变化之数组元素重复

对于二分查找而言,会出现数组元素重复的情况,以下问题的求解建立在数组元素重复的情况下:

1.返回匹配数key的最小下标

#include <stdio.h>
 
int main() {
    int arr[] = {1,3,3,3,3,4,5,6,7};
    int left,right,lenght,key;
    key = 3;
    left = 0;
    lenght = sizeof(arr)/sizeof(arr[0]);
    right = lenght -1;
    while(left <= right){
        //等于的条件不能忽略
        int mid = (left+right)/2;
        if (arr[mid] < key)
            left = mid + 1;
            //目的是用left来寻找key点,不考虑mid = key的情况
        else
            right = mid - 1;
    }
    if ((left < lenght)&&(arr[left] == key))
        printf("%d\n",left);
    return 0;
}

当arr[mid] == key 时,此时直接输出,则无法达到最小坐标。所以我们需要移动left或right。对于移动left而言,显然移动后left将大于mid,更不可能找到最小坐标。所以需要移动right,让left逐渐逼近key的最小下标。

当arr[mid] > key时,此时需要移动right,所以此时不会出现left = key的情况

当arr[mid] < key时,此时需要移动left,前面已经知道mid<key,当left = mid + 1时,(其实类似于单调函数的求值)arr[mid] < arr[left] <=key

与原题对比,我们可以发现,只有arr[mid] == key 时产生了变化,这里有一个显然的结论,对于arr[mid] == key ,当求最小下标时,我们需要下标left<mid,所以只能移动right,当求最大下标时,我们需要right>mid,所以只能移动left。

2.返回匹配数的最大下标

#include <stdio.h>
 
int main() {
    int arr[] = {1,3,3,3,3,4,5,6,7};
    int left,right,lenght,key;
    key = 3;
    left = 0;
    lenght = sizeof(arr)/sizeof(arr[0]);
    right = lenght -1;
    while(left <= right){
        int mid = (left+right)/2;
        if (arr[mid] <= key)
            left = mid + 1;
        else
            right = mid - 1;
    }
    if ((right >= 0)&&(arr[right] == key))
        printf("%d\n",right);
    return 0;
}

3.返回第一个大于匹配数的下标

#include <stdio.h>
 
int main() {
    int arr[] = {1,1,1,1,1,4,5,6,7};
    int left,right,lenght,key;
    key = 3;
    left = 0;
    lenght = sizeof(arr)/sizeof(arr[0]);
    right = lenght -1;
    while(left <= right){
        int mid = (left+right)/2;
        if (arr[mid] < key)
            left = mid + 1;
        else
            right = mid - 1;
    }
        printf("%d\n",arr[left]);
    return 0;
}

这个问题,比较简单,把之前的if条件注释掉就好了。=_=因为我们在循环过程中如果没有找到,left会自己停在第一个大于key的坐标上。

4.查找某个数的出现次数

这个问题就比较进阶,一开始在一篇介绍二分查找的文章里看到求出现次数,怎么想也联系不到二分查找。最后在网上找到了别人写的代码,其实就是两次二分查找,但是试了好几次也没有写对,看来我的蒟蒻路还有很长。

四、轮转循环

何为轮转(循环)有序数组,就比如说{7,8,9,0,1,2,3,4,5,6}就是一个轮转数组,其最小值不在[0]点。对于这样的数组是否可以使用二分查找,?

对于这种问题,我们需要考虑的是他的顺序起点,以{7,8,9,0,1,2,3,4,5,6}为例,他的顺序起点为a[3] = 0;

(其实把它理解成为一个分段函数最容易,但是csdn不好写数学表达式,不好画图,有兴趣的蒟蒻可以私信我)

#include <stdio.h>
int find(int arr[],int left,int right,int key) {
//传参a[]是所求数组,left,right为所求左右下标,key是所求元素
    while (left <= right) {
        int mid = (left + right) / 2;
        if(arr[mid] > key){
            if(arr[mid] < arr[right])//当此式成立时表明元素顺序起点在mid左边。
                right = mid - 1;
            else {//元素顺序起点在mid右边,但是key有可能在mid左边有可能在mid右边
                if (key > arr[left])//在左边
                    right = mid - 1;
                else
                    left = mid + 1;
            }
        }
        else if(arr[mid] < key){
            if(arr[mid] > arr[left])//在mid左边是递增的(顺序起点在[0]或右边)
                left = mid + 1;
            else{//顺序起点在左边且不在[0]上
                if (key > arr[right])//在mid左边
                    right = mid - 1;
                else
                    left = left + 1;
            }
        }
        else//当相等时直接返回mid
           return mid;
 
    }
 
}
int main() {
    int left,right,key,mid;
    int arr[] = {7,8,9,0,1,2,3,4,5,6};
    left = 0;
    right =( sizeof(arr)/sizeof(arr[0]) ) - 1;
    key = 4;
    mid = find(arr,left,right,key);
    printf("峡谷吴彦祖 %d",mid);
    return 0;
}

五、杨氏矩阵

在剑指offer的项目练习上,有在杨氏矩阵中对一个数进行查找的案例。题中所给出的解题思路是利用杨氏矩阵每行递增,每列递增的性质每次排除某行或某列。而在我的实践过程中,其可以采用二分查找的思路,对大规模的矩阵进行更加优化的查找。以下是此方法的代码:

int yang_matrix_2(int arr[X][Y],int num) {
    int x = 0;
    int y = Y;
    int a = x;
    int b = y;
    do{
       a = x;b = y;
        if (arr[x][y] > num) {//在列上进行折半查找
            int left = 0;
            int right = Y-1;
            while (left <= right){
                int mid = (((left ^ right) >> 1)+(left & right));
                if (arr[x][mid] > num)
                    right = mid - 1;
                else {
                    if (arr[x][mid] < num)
                        left = mid + 1;
                    else
                        return 1;}
            }
            y = left;
            x--;
        }
        else{
            if(arr[x][y] < num){
                int left = 0;
                int right = X-1;
                while (left <= right){
                    int mid = (((left ^ right) >> 1)+(left & right));
                    if (arr[mid][y] > num)
                        right = mid - 1;
                    else {if (arr[mid][y] < num)
                            left = mid + 1;
                        else
                            return 1;}
                }
                x = right;
                ++y;
            }//在行上进行折半查找
            else
                return 1;
        }
    }while ((x != a) && (y != b));
    return 0;
}

六、用二叉树来描述二分查找

二分查找过程可用二叉树来描述:把当前查找区间的中间位置上的结点作为根,左子表和右子表中的结点分别作为根的左子树和右子树。

由此得到的二叉树,称为描述二分查找的判定树(Decision Tree)或比较树(Comparison Tree)。以下图为例

七、二分查找的概率问题

我们需要引入一个概念叫做平均查找长度ASL,为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值称为查找算法在查找成功时的平均查找长度()。二分查找的ASL = log2(n+1)-1。

到此这篇关于C语言基础之二分查找知识最全汇总的文章就介绍到这了,更多相关C语言二分查找汇总内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++实现LeetCode(112.二叉树的路径和)

    C++实现LeetCode(112.二叉树的路径和)

    这篇文章主要介绍了C++实现LeetCode(112.二叉树的路径和),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • 基于memset()函数的深入理解

    基于memset()函数的深入理解

    本篇文章是对memset()函数又进行了深一步的了解,需要的朋友参考下
    2013-05-05
  • VC WinExec打开指定程序或者文件的方法

    VC WinExec打开指定程序或者文件的方法

    使用WinExec命令打开指定程序或者文件的参数说明
    2008-11-11
  • C语言字符串操作总结大全(超详细)

    C语言字符串操作总结大全(超详细)

    本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下
    2013-05-05
  • 利用C++如何覆盖或删除指定位置的文件内容

    利用C++如何覆盖或删除指定位置的文件内容

    这篇文章主要给大家介绍了关于利用C++如何覆盖或删除指定位置的文件内容,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面跟着小编来一起学习学习吧。
    2017-08-08
  • C语言自定义军旗游戏源码

    C语言自定义军旗游戏源码

    这篇文章主要为大家详细介绍了C语言自定义军旗游戏源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • C++面试基础之static关键字详解

    C++面试基础之static关键字详解

    这篇文章主要给大家介绍了关于C++面试基础之static关键字的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-02-02
  • C++中四种加密算法之DES源代码

    C++中四种加密算法之DES源代码

    本篇文章主要介绍了C++中四种加密算法之DES源代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。
    2016-11-11
  • C++中memset函数用法详解

    C++中memset函数用法详解

    这篇文章主要介绍了C++中memset函数用法,结合实例形式详细分析了memset函数的功能、使用方法与相关注意事项,需要的朋友可以参考下
    2016-06-06
  • 详解C++中String类模拟实现以及深拷贝浅拷贝

    详解C++中String类模拟实现以及深拷贝浅拷贝

    这篇文章主要介绍了详解C++中String类模拟实现以及深拷贝浅拷贝的相关资料,希望通过本文能帮助到大家,让大家实现这样的方法,需要的朋友可以参考下
    2017-10-10

最新评论