二分算法超详细解析(适合算法新手)

二分搜索算法初级解析

1 需要考虑的几个点

提供的待搜索集合一般是非递减排列

二分搜索算法需要考虑的几个点:
a) 搜索区间的左右闭合问题:
[0,nums.length-1]即左闭右闭区间,[0,nums.length)即左闭右开区间

b) 对于迭代结束条件问题:

  • [0,nums.length-1]区间迭代结束条件为left>right
  • [0.nums.length)区间迭代结束条件为left>=right,准确来说是left==right

c) 对于迭代时左右指针变换问题:

  • [0,nums.length-1]区间左指针变换为mid+1,右指针变换为mid-1
  • [0,nums.length)区间左指针变换为mid+1,右指针变换为mid
    (因为mid已经在该迭代轮次时搜索了,变换时闭合区间不要再次包含mid,开放区间要包含mid)

d) 二分查找特定值一般使用左闭右闭区间,找到等于该值的位置即可
(有重复元素存在时无所谓该位置是不是对应第一个元素)

e) 二分查找特定值的左/右边界,也就是该元素第一次/最后一次出现的位置,一般使用左闭右开区间
(查找边界问题需要考虑该元素是否真正存在,以及迭代结束时指针越界问题,见下文详解)

2 二分查找特定值

a) 这是最典型最简单的二分搜索算法题,记录下其通用的算法框架:

int binarySearch(int[] nums, int target) {
    int left = 0; 
    // 左闭右闭区间
    int right = nums.length - 1;

    // 注意迭代结束条件
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            // 注意
            left = mid + 1;
        else if (nums[mid] > target)
            // 注意
            right = mid - 1;
    }
    return -1;
}

3 二分查找左右边界值

a) 该类型算法最大的难点在于:

  • 判断期望返回的元素在迭代结束后的准确位置
  • 判断需要考虑该元素是否真正存在于集合
  • 若元素存在,需要考虑该元素在集合首尾边界位置的情况
  • 若元素不存在,需要考虑迭代结束时元素和target大小关系,进而作返回值的处理
  • 若元素不存在,需要考虑迭代结束时,左右指针越界导致返回时出错

b) 先给出算法基本框架

【查找左边界】

int left = 0, right = nums.length;
while(left<right){
    mid = left + (right-left)/2 ;
    // 如果搜索到target相等的元素,意味着至少需要向mid左部搜索
    if(nums[mid] == target){
        right = mid;
    }
    // 如果搜索到target小的元素,意味着至少需要向mid右部搜索
    else if(nums[mid] < target){
        left = mid+1;
    }
    else if(nums[mid] > target){
        right = mid;
    }
}

【查找右边界】

int left = 0, right = nums.length;
while(left<right){
    mid = left + (right-left)/2 ;
    // 如果搜索到target相等的元素,意味着至少需要向mid右部搜索
    if(nums[mid] == target){
        left = mid+1;
    }
    // 如果搜索到target小的元素,意味着至少需要向mid右部搜索
    else if(nums[mid] < target){
        left = mid+1;
    }
    else if(nums[mid] > target){
        right = mid;
    }
}

c) 逐个考虑上面列出的难点问题

  • 返回值:
    对于左边界问题,若在迭代结束时找到target的左边界,有可能是最后一轮 left== right =mid+1 得到,也有可能是 left== right =mid

    只有可能最后迭代是走这两个分支后结束,即返回left或者right就是target(这里默认返回左指针)
    return nums[left];
    对于右边界问题,若在迭代结束时找到target的右边界,有可能是最后一轮 left == right = mid 得到,也有可能是 left == right = mid+1 得到,但这里的right不可能指向target,所以left向右搜索一步后和right相等


    只有可能最后迭代是走这两个分支后结束,与target相同的元素只有可能是left-1或者right-1(这里默认返回左指针)
    return nums[left-1];

  • 若元素存在,考虑其位于集合首位边界的情况
    由于左边界问题返回的是left,若元素存在,left==right找到target时不会越界;

  • 若元素不存在时,有两种情况:
    情况一:迭代结束时,两指针停在集合中。由于左边界问题迭代结束时可能是left == right = mid+1,或者是 left == right = mid,由判断分支知道这两种情况指向的都是刚好大于target的元素,即指向大于target的第一个元素;由于右边界问题迭代结束时可能是left == right = mid+1,或者是 left == right = mid,由判断分支知道这两种情况left-1指向的都是小于target的最大元素,即指向小于target的最后一个元素
    情况二:迭代结束时,左边界问题中的left == right = nums.length,也就是left一直在找right前进,逼近右边区域,过程中搜索到的元素均小于target,该情况也属于搜索失败了;右边界问题中的left == right = 0,也就是right一直在找left后退,过程中搜索到的元素均大于target,属于搜索失败
    综上所述

  • 搜索失败有两种可能:
    第一种可能:两指针停在集合中
    return nums[left]==target?left:-1; //左边界
    return nums[left-1]==target?left-1:-1; //右边界
    第二种可能:两指针停在集合首尾边界位置
    if(left==nums.length) return -1; //左边界
    if(left==0) return -1; //右边界

提供二分搜索左右边界的代码框架:
【左边界】

private int leftBoundSearch(int[] nums, int target){
        int left = 0, right = nums.length;
        while(left < right){
            int mid = left + (right - left) / 2;
            if(nums[mid] == target) right = mid;
            else if(nums[mid] < target) left = mid + 1;
            else if(nums[mid] > target) right = mid;
        }
        if (left == nums.length) {
            return -1;
        }
        return nums[left] == target ? left : -1;
    }

【右边界】

private int rightBoundSearch(int[] nums, int target){
        int left = 0, right = nums.length;
        while(left < right){
            int mid = left + (right - left) / 2;
            if(nums[mid] == target) left = mid + 1;
            else if(nums[mid] < target) left = mid + 1;
            else if(nums[mid] > target) right = mid;
        }
        if (left == 0 ) {
            return -1;
        }
        return nums[left-1] == target ? left-1 : -1 ;

    }
posted @ 2024-09-04 15:32  CandyWang-  阅读(219)  评论(0)    收藏  举报