二分查找从0到精通

一、二分查找的二种写法

        1.[left,right]左闭右闭

        2.[left,right) 左闭右开

二、二分查找的二类需求

        1.在最左边找(升序情况下的第一个target)

        2.在最右边找(升序情况下的最后一个target)

        看到根据区间来写二分,注意力好的朋友应该可以想到数组,我们通常处理[0,len-1]惑者[0,len)。是滴,就是这样的。

1.左闭右闭

int binary_search(int arr[],int len,int x){
    int l=0;
    int r=len-1;
    while (l<=r){//左闭右闭情况l=r有意义!!
        int mid=l+(r-l)/2;//防止l+r数值溢出
        if (arr[mid]==x){
            return mid;//找到了
        }else if (arr[mid]>x){
            r=mid-1;//左区间,更新右边界
        }else l=mid+1;//右区间,更新左边界
    }
    return -1;
}

        举个例子来帮助理解,假设目标值在左区间,更新右边界r=mid-1,即[left,right]->[left,mid-1],不漏值;

2.左闭右开

int binarysearch(int arr[],int len,int x){
    int l=0;
    int r=len;
    while (l<r){
        int mid=l+(r-l)/2;
        if (arr[mid]==x){
            return mid;//找到了
        }else if (arr[mid]>x){
            r=mid;//左区间,更新右边界
        }else l=mid+1;//右区间,更新左边界
    }
    return -1;
}

        左闭右开情况下l=r无意义,所以循环条件为l<r。很简单,举个例子来帮助理解,假设目标值在左区间,更新右边界r=mid;为什么不-1呢?很简单,如果-1,那么[left,right]->[left,mid-1),若mid-1恰好是target呢?\滑稽


二分查找我们已经会了,但如果给定1,3,7,7,7,7,9,83这个序列,求7第一次出现的位置。哦豁,直接一手二分好像不得行咯~

我们来复习一下二分的流程

假设我们要在有序数组中查找目标元素,数组中的元素可以是重复的。当我们比较中间元素与目标元素大小时,有三种情况可能发生:

  1. 中间元素等于目标元素:找到了目标元素,返回中间元素的索引值。

  2. 中间元素大于目标元素:目标元素在中间元素的左边,继续在左半部分进行二分查找。

  3. 中间元素小于目标元素:目标元素在中间元素的右边,继续在右半部分进行二分查找。

显然我们发现问题出在步骤1,找到目标元素后不要急,往左/右再找看看,直到出现第一个不同元素。我们可以通过以下两种方式来处理:

  1. 查找第一个与目标元素相等的元素:在比较中间元素与目标元素大小时,如果相等,继续在左半部分进行查找,直到找到第一个与目标元素不相等的元素。这样可以得到目标元素在数组中的最左边的索引值。

  2. 查找最后一个与目标元素相等的元素:在比较中间元素与目标元素大小时,如果相等,继续在右半部分进行查找,直到找到第一个与目标元素不相等的元素。这样可以得到目标元素在数组中的最右边的索引值。

左闭右闭区间写法来解决这个问题

int binary_search(int arr[],int len,int x){
    int l=0;
    int r=len-1;
    int result=-1;
    while (l<=r){
        int mid=l+(r-l)/2;
        if (arr[mid]==x){
            result=mid;//更新结果
            r=mid-1;//继续在左半部分查找
        }else if (arr[mid]>x){
            r=mid-1;
        }else l=mid+1;
    }
    return result;
}

        在这个代码中,我们使用了变量result来保存第一个与目标元素相等的索引值。当遇到与目标元素相等的元素时,我们将result更新为当前的中间元素的索引,然后继续在左半部分进行查找,直到找到最左边的索引值。如果数组中不存在目标元素,result的初始值-1将会被保留。

在最右边找同理,就不做介绍了。

有误的地方请大家评论区交流


更新一下对二分新的理解:

前面我们已经论述了如何实现查找第一个出现的target或最后一个出现的target。

可以发现这二个if分支是可以合并的,那么我们就得到了更优的代码,发现已经有大佬有很好的总结,如图(图片来自该大佬博客)

参考:二分查找 & 二分答案 万字详解,超多例题,带你学透二分。-CSDN博客

可以肯定的是这种方法肯定适用,引用文章使用的左闭右开写法,抽象程度有点高,大家可以看我这篇文章从0到精通,然后去这位大佬博客下刷刷例题。

还是写一个左闭右开的模板,代码示例如下

int binary_search(int arr[], int len, int x) {
	int l = 0, r = len;
	while (l < r) {
		int mid = l + (r - l) / 2;
		if (arr[mid] >= x) r = mid;
		else l = mid + 1;
	}
	return l;
}

这个check()看了文章的应该都懂。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值