二分查找 模板(二分)

菜鸡一个,在这里总结以下二分的使用方法和几个模板

转自https://blog.csdn.net/weixin_41183791/article/details/83959740

特别要注意各个函数的返回值

三种基本版本:

1.1二分查找原始版--查找某个数的下标(任意一个)

在有序数组中查找某个数,找到返回数的下标,存在多个返回任意一个即可,没有则返回-1;所有程序采用左右均为闭区间,即函数n为最后一个元素下标,典型代码如下

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

1.2查找第一个大于等于某个数的下标

例:int[] a = {1,2,2,2,4,8,10},查找2,返回第一个2的下标1;查找3,返回4的下标4;查找4,返回4的下标4。如果没有大于等于key的元素,返回-1。

下面是代码,改动只有两处:

int bsearch(int a[], int n, int key){
    int low = 0;
    int high = n;
    int mid = 0;
    while(low <= high) {
        mid = low + ((high-low) >> 1);
        if(key <= a[mid]) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return low <= n ? low : -1;
}

解释:
1、条件为key<=a[mid],意思是key小于等于中间值,则往左半区域查找。如在 {1,2,2,2,4,8,10}查找2,第一步,low=0, high=6, 得mid=3, key <= a[3],往下标{1,2,2}中继续查找。

2、终止前一步为: low=high,得mid = low,此时如果key <= a[mid],则high会改变,而low指向当前元素,即为满足要求的元素。如果key > a[mid],则low会改变,而low指向mid下一个元素。

3、如果key大于数组最后一个元素,low最后变为n+1,即没有元素大于key,需要返回 -1。


1.3 查找第一个大于某个数的下标

例:int a[] = {1,2,2,2,4,8,10},查找2,返回4的下标4;查找3,返回4的下标4;查找4,返回8的下标5。如果没有大于key的元素,返回-1。
 

int bsearch(int a[], int n, int key){
    int low = 0;
    int high = n;
    int mid = 0;
    while(low <= high) {
        mid = low + ((high-low) >> 1);
        if(key < a[mid]) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return low <= n ? low : -1;
}

以上原型的扩展应用

2.1 查找数组中某个数的位置的最小下标,没有返回-1

直接用上面1.2,但需要处理一下,即当返回的low位置不等于key时,也返回-1。如下:

int bsearch(int a[], int n, int key){
    int low = 0;
    int high = n;
    int mid = 0;
    while(low <= high) {
        mid = low + ((high-low) >> 1);
        if(key <= a[mid]) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return (low <= n) && (a[low] == key) ? low : -1;
}

2.2 查找数组中某个数的位置的最大下标,没有返回-1

直接用上面1.3,得到的下标 - 1即可,但也需要处理一下,即不用判断low <= n,而是判断low-1>=0,且返回的low-1位置等于key时,才返回位置low - 1。如下:

int bsearch(int a[], int n, int key){
    int low = 0;
    int high = n;
    int mid = 0;
    while(low <= high) {
        mid = low + ((high-low) >> 1);
        if(key < a[mid]) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return (low - 1 >= 0 && (a[low - 1] == key))? low - 1: -1;
}

2.3 查找数组中小于某个数的最大下标,没有返回-1

直接用上面1.2,返回的low-1位置即为可能的位置。也需要处理一下,需要low-1>=0。

int firstLess(int a[], int n, int key) {
    int low = 0;
    int high = n;
    int mid = 0;
    while (low <= high) {
        mid = low + ((high - low) >> 1);
        if (key <= a[mid]) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return (low - 1 >= 0) ? low - 1 : -1;
}

2.4 查找数组中某个数的出现次数

直接使用1.2,1.3即可,但需要修改1.2,1.3的返回条件,当low>n时,直接返回low。举例说明:int a[] = {1,2,2,2,4,8,10},查找10的个数,根据1.2程序得到low = 6,而1.3程序返回值 = -1,不符合。因此需要将1.3得到的low = n + 1值返回即可。同理,如果查找15,此时1.2程序返回值为-1,也得改为直接返回low = n+1。
使用1.2,1.3,分别求得下界first和上界last,两个相减即(last - first)是key出现次数。如下:
 

int getCount(int a[], int n, int key) {
    // n + 1 个数
    int first = firstGreatOrEqual2(a, n, key);
    int last = firstGreat2(a, n, key);
    return last - first;
}
 
int firstGreatOrEqual2(int a[], int n, int key) {
    int low = 0;
    int high = n;
    int mid = 0;
    while (low <= high) {
        mid = low + ((high - low) >> 1);
        if (key <= a[mid]) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return low;
}
 
int firstGreat2(int a[], int n, int key) {
    int low = 0;
    int high = n;
    int mid = 0;
    while (low <= high) {
        mid = low + ((high - low) >> 1);
        if (key < a[mid]) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return low;
}

猜你喜欢

转载自blog.csdn.net/shuaizhijun/article/details/88999551
今日推荐