详解整数集上的二分法

二分的基础用法是在单调序列或单调函数中进行查找

本文章暂时只讨论整数集合上的二分查找

目录

一.两种形式的写法

二.为什么使用右移运算而不是整数除法

三.如何处理无解的情况


一.两种形式的写法

               以单调递增序列为例,有两种情况:

1.在序列a中查找≥x的数中的最小的一个;

2.在序列a中查找≤x的数中的最小的一个;

值得注意的是,二分法并不需要考虑这个序列的个数是奇数还是偶数

情况1:在序列a中查找≥x的数中的最小的一个

#include<stdio.h>
int main()
{
    int a[7]={1,3,5,7,9,13,17};
    int x;    //x为我们要查找的条件范围
    int l,r;  //[l,r]为二分查找的区间
    int mid;  //二分的中间值
    scanf("%d",&x);
    //开始查找
    while(l<r)  //循环结束的条件是l=r,最后我们查找的数就是在l(或者是r)这个位置
    {
        int mid=(l+r)>>1;
                //位运算,相当于除以2再向下取整  *为什么这里用位运算而不是直接用除法呢?
        if(a[mid]>=x) r=mid;  //如果中间的数大于等于我们要找的范围,那么我们只要前一半段
        else l=mid+1;   //如果中间的数小于我们要找的范围,那么我们只要后面一半段
    }
    //输出结果
    printf("%d\n",a[l]);
    return 0;
}

情况2:在序列a中查找≤x的数中的最大的一个

#include<stdio.h>
int main()
{
    int a[7]={1,3,5,7,9,13,17};
    int x;    
    int l,r;  
    int mid;  
    scanf("%d",&x);
    //开始查找
    while(l<r)  
    {
        int mid=(l+r+1)>>1;
                
        if(a[mid]<=x) l=mid;  
        else r=mid-1;   
    }
    //输出结果
    printf("%d\n",a[l]);
    return 0;
}

两种情况的区别是:缩小范围时所取的mid,l,r有所不同:

1.r=mid,l=mid+1,mid=(l+r)>>1

2.l=mid,r=mid-1,mia=(l+r+1)>>1

二.为什么使用右移运算而不是整数除法

使用整数除法的话,在二分值域包含负数时不能正常工作

而右移运算不会存在这种问题

三.如何处理无解的情况

把a数组的一个越界的下标包含进来,如果最后二分终止在扩大后的这个越界下标上,则说明a中不存在所求的这个数;

以情况1( 在序列a中查找≥x的数中的最小的一个 )为例子

我们把它扩大到[1,n+1]

#include<stdio.h>
int main()
{
    int a[8]={1,3,5,7,9,13,17};
    int x;
    int l,r;
    int mid;
    scanf("%d",&x);
    //开始查找
    while(l<r)
    {
        int mid=(l+r)>>1;

        if(a[mid]>=x) r=mid;
        else l=mid+1;
    }
    //输出结果
    if(l=7) printf("No Found\n");
    else printf("%d\n",a[l]);
    return 0;
}

对于情况2,我们把范围扩大到[0,n]

猜你喜欢

转载自blog.csdn.net/qq_59414507/article/details/121290103