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语言二分查找汇总内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Linux页面置换算法的C语言实现

    Linux页面置换算法的C语言实现 编写算法,实现页面置换算法FIFO.LRU.OPT:针对内存地址引用串,进行页面置换算法进行页面置换. 其中,算法所需的各种参数由输入产生(手工输入或者随机数产生):输出内存驻留的页面集合,缺页次数以及缺页率. #include <stdio.h> //#include <conio.h> #include <stdlib.h> #include <time.h>//随机数 #define Myprintf printf(

  • C语言快速排序与二分查找算法示例

    本文实例讲述了C语言二分排序与查找算法.分享给大家供大家参考,具体如下: 题目:首先产生随机数,再进行快速排序,再进行二分查找. 实现代码: #include <stdio.h> #include <stdlib.h> #include <time.h> void quiksort(int a[],int low,int high) { int i = low; int j = high; int temp = a[i]; if( low < high) { wh

  • C语言实现页面置换算法

    本文实例为大家分享了C语言实现页面置换算法的具体代码,供大家参考,具体内容如下 操作系统实验 页面置换算法(FIFO.LRU.OPT) 概念: 1.最佳置换算法(OPT)(理想置换算法):从主存中移出永远不再需要的页面:如无这样的页面存在,则选择最长时间不需要访问的页面.于所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率. 2.先进先出置换算法(FIFO):是最简单的页面置换算法.这种算法的基本思想是:当需要淘汰一个页面时,总是选择驻留主存时

  • C语言的进制转换及算法实现教程

    1.其他进制转十进制 1.1.二进制转十进制 转换规程: 从最低位开始,将每个位上的数提取出来,乘以2的(位数-1)次方,然后求和,例如: 二进制 1011 = 1*2^0 + 1*2^1 + 0*2^2 + 1*2^3 = 1 + 2 + 0 + 8 = 11 1.2.八制转十进制 转换规则: 从最低位开始,将每个位上的数提取出来,乘以8的(位数-1)次方,然后求和,例如: 八进制 0123 = 3*8^0 + 2*8^1 + 1*8^2 = 3+16+64 = 83 1.3.十六进制转十进制

  • C语言编程中实现二分查找的简单入门实例

    架设有一个数组 v 已经按升序排列了,数组 v 有 n=20 个元素.数组中有个元素 x,如何知道 x 位于该数组的第几位呢? 解决这个问题的一个普遍方法就是二分查找法.下面是程序: #include <stdio.h> int binsearch(int x, int v[], int n); main() { int i, result, n; int wait; int x = 17; // 需要查找的数值 int v[19]; // 定义一个数组 // 给数组赋值 for(i = 0;

  • C语言实现九大排序算法的实例代码

    直接插入排序 将数组分为两个部分,一个是有序部分,一个是无序部分.从无序部分中依次取出元素插入到有序部分中.过程就是遍历有序部分,实现起来比较简单. #include <stdio.h> void insertion_sort(int arr[], int array_length) { for (int i = 0; i < array_length; ++i) { int data = arr[i]; int j = 0; while (arr[j] < arr[i]) { j

  • C语言使用stdlib.h库函数的二分查找和快速排序的实现代码

    快速排序: 复制代码 代码如下: #include <stdlib.h>#include <stdio.h>#include <string.h> #define LENGTH(x) sizeof(x)/sizeof(x[0]) /**输出数组元素*\param arr:指向数组的指针*\param len:数组元素的个数*/void print(char (*arr)[10],int len){    int i;    for (i=0;i<len;i++) 

  • C语言数据结构中二分查找递归非递归实现并分析

    C语言数据结构中二分查找递归非递归实现并分析 前言: 二分查找在有序数列的查找过程中算法复杂度低,并且效率很高.因此较为受我们追捧.其实二分查找算法,是一个很经典的算法.但是呢,又容易写错.因为总是考虑不全边界问题. 用非递归简单分析一下,在编写过程中,如果编写的是以下的代码: #include<iostream> #include<assert.h> using namespace std; int binaty_search(int* arr, size_t n, int x)

  • C语言二分查找算法及实现代码

    二分査找也称折半査找,其优点是查找速度快,缺点是要求所要査找的数据必须是有序序列.该算法的基本思想是将所要査找的序列的中间位置的数据与所要査找的元素进行比较,如果相等,则表示査找成功,否则将以该位置为基准将所要査找的序列分为左右两部分.接下来根据所要査找序列的升降序规律及中间元素与所查找元素的大小关系,来选择所要査找元素可能存在的那部分序列,对其采用同样的方法进行査找,直至能够确定所要查找的元素是否存在,具体的使用方法可通过下面的代码具体了解. #include <stdio.h> binar

  • C语言实现页面置换 先进先出算法(FIFO)

    本文实例为大家分享了C语言实现页面置换算法的具体代码,供大家参考,具体内容如下 一.设计目的 加深对请求页式存储管理实现原理的理解,掌握页面置换算法中的先进先出算法. 二.设计内容 设计一个程序,有一个虚拟存储区和内存工作区,实现下述三种算法中的任意两种,计算访问命中率(命中率=1-页面失效次数/页地址流长度).附加要求:能够显示页面置换过程. 该系统页地址流长度为320,页面失效次数为每次访问相应指令时,该指令对应的页不在内存的次数.    程序首先用srand()和rand()函数分别进行初

随机推荐