[算法系列]吃透二分查找:经典题目+解题思路+模板

本文深入探讨二分查找算法,通过多个LeetCode题目展示不同变体,包括寻找目标值的第一个和最后一个出现位置、旋转数组中的最小值、在旋转排序数组中寻找目标等。详细解析了每个问题的解题思路和模板,强调了二分查找在有序数组中的应用及边界处理策略。
摘要由CSDN通过智能技术生成

0.最原始的二分查找

本文勉强算是递归系列的第八篇文章吧。不过实际上本文是二分专题。以多道LC上题介绍二分的解题思路与模板。

沿着递归的思路,今天我们来谈谈二分查找

严格来说,二分查找和递归联系不算太紧密,本文介绍的主要是非递归版本的二分查找类型习题的解析,话不多说,先来看看原始的BinarySearch吧!

给定一个没有重复元素的有序数组arr,查找目标值target的下标,若arr中没有则返回-1

由于是递归系列,那我们从递归版的二分开始:

(ps:本文代码中中部分边界判空条件省略)

    /* 递归版二分查找 */
    public static int binarySearchRecur(int[] arr ,int target, int left , int right){
   
        if(left >= right) return -1;
        int mid = (right + left) >> 1;

        if(arr[mid] == target )
            return mid;
        if(arr[mid] > target)
            return binarySearchRecur(arr, target , left , mid -1);
        if(arr[mid] < target)
            return binarySearchRecur(arr,target , mid + 1, right);
        return  -1;
    }

下面改成非递归版的,也是我们今天得重头戏

    /*
    迭代版二分查找 LeetCode704
     */
    public static int binarySearchIter(int[] arr, int target) {
   
        int left = 0, right = arr.length - 1;
        while (left <= right){
   
            int mid = left + ((right - left) >> 1);
            if(arr[mid] == target)
                return mid;
            if(arr[mid] > target)
                right = mid - 1;
            if(arr[mid] < target)
                left = mid + 1;
        }
        return -1;
    }

1.寻找第一个/最后一个出现target的位置

  • 给定一个可能有重复元素的有序数组arr,查找目标值target第一次出现的下标,若arr中没有则返回-1

    比如:[1,3,4,5,5,5,5,7] ,target = 5

    返回 :3

    [2,5,6,6,6,8],target = 7

    返回:-1

  • 给定一个可能有重复元素的有序数组arr,查找目标值target最后一次出现的下标,若arr中没有则返回-1

    比如:[1,3,4,5,5,5,5,7] ,target = 5

    返回 :6

这是二分中很经典的一类问题,求区间。

第一个题目问法有几种形式:

  • 求第一个出现target的下标
  • 求小于target的个数
  • 求小于target的最大元素

两种种说法做法几乎的!!!其中第1和第2点完全相同,只是换了种说法,第3点在1,2结果减1即可

1.1求第一个出现target的下标 / 小于target的个数

在基本二分的框架中稍作修改:

    /* 求第一个出现target的下标 / 小于target的个数  */
	public static int findFirstTarget(int[] arr , int target){
   
        int left = 0 , right = arr.length - 1;
        while (left <= right) {
   
            int mid = left + ((right - left) >> 1);
            if(arr[mid] == target)
                right = mid - 1;	//注意1
            if(arr[mid] > target)
                right = mid - 1;
            if(arr[mid] < target)
                left = mid + 1;
        }
        if(left >= arr.length ||arr[left] != target) //注意2
            return -1;
        return left;
    }

与上面纯正二分相比,注意代码中的两个地方:

  1. arr[mid] == target时,并不是直接返回mid,为啥?因为这时并不知道mid所在位置是否为target的第一个(他的左边可能还有target)。

    因此将右边界缩小,往左边找。

  2. while结束时,left停留在righ+1的位置。

    若:right始终在最右边,则left=arr.length,不存在该下标(target比所有元素都大,此时未找到target)

    若:left最终停留在中间位置,但arr[left] != target,(此时找到的只是合适的插入target的位置,同样未找到target)

1.2求最后一个出现target的下标

和上面类似,直接上代码:

    /*
        最后出现target的位置
     */
    public static int findLastTarget(int[] arr , int target){
   
        int left = 0 , right = arr.length -1;
        while (left <= right){
   
            int mid = left + (
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值