二分搜索总结

看了这篇帖子,对思路很简单,细节是魔鬼感同身受,故自己来做个总结。

1.普通的二分搜索

在一个一维有序数组 nums 中查找 target 是否存在,存在返回 index ,不存在返回 -1:

public int binarySearch(int[] nums, int target) {
        //省去了特殊条件判断
        int len = nums.length;
        int left = 0;
        int right = len - 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
                right = mid - 1;
        }
        return -1;
    }

需要注意的是 right 的赋值及 while 条件:我们设定 right = len - 1 时,二分搜索的区间为 [left,right] ,搜索循环终止的条件为:①找到 target ,即 nums[mid] == target ,则返回 mid;②当 left > right 时表明未找到 target,终止循环,返回-1。那么为什么循环条件是 left <= right:left < right 显然是必须的,我们再考虑临界情况,当 left + 1 = right 时进入循环,这时求得 mid = left,若 nums[mid] == target,程序直接返回 mid;若 nums[mid] < target,则需要右移 left,left = mid + 1 = left + 1 = right,此时 left = right ,搜索空间 [left,left](或 [right,right]) 并不为空,若循环条件中没有等于,则遗漏了该处元素的比较(当然也可以在循环外补充上此处比较);若 nums[mid] > target,则需要左移 right,right = mid - 1 = left - 1,此时 left > right,搜索空间 [left,right]为空,循环结束。

2.二分搜索左(右)边界

先来说明一下左边界和右边界,比如 nums = {1,2,2,2,2,3,4},那么当 target = 2 时,求左边界的返回值为 1(最左边的2),右边界的返回值为 4(最右边的2)。对于这种情况,基本的二分搜索返回的是其中的一个 2 而不是最左(右)边的。

先来看搜索左边界:

public static int binarySearchLeft(int[] nums, int target) {
        int len = nums.length;
        int left = 0;
        int right = len;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                right = mid;
            } else if (nums[mid] < target)
                left = mid + 1;
            else
                right = mid;
        }
        return left;
    }

该种情况下我们的搜索区间为 [left,right) ,所以赋值 right = len 且循环条件为 left < right,当 left = right 时跳出循环[left,left) 为空不会错过解。同样考虑当 left + 1 = right 时,mid = left,若 nums[mid] >= target,right = mid = left 循环结束;若nums[mid] < target,left = left + 1 = right 循环结束。可以看到最终循环结束,left 和 right 都指向数组中第一个大于等于 target 的元素。

右边界和左边界类似,当 nums[mid] == target 时,不要立即返回,而是增大「搜索区间」的下界 left,使得区间不断向右收缩,达到锁定右侧边界的目的:

public static int binarySearchLeft(int[] nums, int target) {
        int len = nums.length;
        int left = 0;
        int right = len;
        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
                right = mid;
        }
        return left - 1;
    }

  同样分析 left + 1 = right 时,mid = left,若 nums[mid] >= target,left = mid + 1 = left + 1 = right 循环结束;若nums[mid] < target,right = mid 循环结束。可以看到最终循环结束,left-1 和 right-1 都指向数组中最后一个小于等于 target 的元素。

3.总结

与求边界的二分查找,最大的区别时搜索空间不同,所以定义左右标记也不同。

基本二分查找
left = 0;
right = len - 1;
循环条件:left <= right
if (nums[mid] == target) 
    return mid;
else if (nums[mid] < target)
    left = mid + 1;
else
    right = mid - 1;
左边界
left = 0;
right = len;
循环条件:left < right
if (nums[mid] == target) 
    right = mid;
else if (nums[mid] < target)
    left = mid + 1;
else
    right = mid;
右边界
left = 0;
right = len;
循环条件:left < right

 

if (nums[mid] == target)
    left = mid + 1;
else if (nums[mid] < target)
    left = mid + 1;
else
    right = mid;

题目1:寻找 nums 中第一个 >= target 的数或者小于 target 的数有几个等情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值