【力扣刷题笔记】二分法查找以及左右边界搜索

这是跟着代码随想录的顺序学习算法的第一天,学习一下才发现自己多菜=-=。

以下是学习题解时自己的一些理解与笔记,有错误欢迎指正与讨论。


二分法

参考相关链接:

704. 二分查找

35. 搜索插入位置

34. 在排序数组中查找元素的第一个和最后一个位置

代码随想录

力扣题解-labuladong


笔记

二分法查找需要满足有序,且无重复元素

1、注意防止溢出
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2

2、位操作符
int middle = left + ((right - left) >> 1); // >> 位操作符 右移1位

3、关于 while 的终止条件和 right 的初始化之间的关系

while<= 时,意味着终止条件应为 left == right + 1 ,即为 左闭右闭,故初始化时 right = nums.size() - 1

while< 时,意味着终止条件应为 left == right,即为 左闭右开,故初始化时 right = nums.size()

因为 leftright 是逐渐靠近的,会首先达到left == right,再进一步达到 left == right + 1

下面先考虑达到 left == right 时的情况,左闭右开的区间已不符合要求,即不会存在 [A, A) 的情况,需要终止循环;但此时左闭右闭区间仍然符合要求,故不需要终止循环。

下面再考虑达到 left == right + 1 时的情况,左闭右闭区间已经不符合要求,即不会存在 [A+1, A] 的情况,故此时才需要终止循环。


4、left = mid + 1right = mid - 1right = mid

因为 left 是闭的,而此时的 mid 是不可能取到的值,所以区间不能包含 mid 的存在,故left = mid + 1right 同理可得何时需要取 mid - 1


5、如何搜索左边界?

左闭右开

//...
if (nums[mid] == target) {
    right = mid;
}
//...
while (left < right) {
    //...
}
// target 比所有数都大
if (left == nums.length) {
    return -1;
}
// target 在搜索区间的左边(不一定取到)
return nums[left] == target ? left : -1;

第一段代码这样做能搜索左边界,因为此时选的区间形式为左闭右开,while终止条件为 left == right ,所以如果此时的 right 取到了左边界,则将会一直在左半部分进行循环搜索 target,直到 left 收敛到 right ,或者 left 也取到了 target

左闭右闭

//...
if (nums[mid] == target) {
    right = mid - 1; //收缩右侧边界
}
//...
while (left < right) {
    //...
}
// 检查出界情况
if (left >= nums.length || nums[left] != target){
    return -1;
}
return left;

此时选的区间形式为左闭右闭,while终止条件为 left == right + 1 ,故需要 right == mid - 1

两种方法完整代码如下:

// 左闭右开区间,搜索左侧边界

int left_bound(int[] nums, int target) {
    int left = 0,right = nums.length;
    // 搜索区间为 [left, right)
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            // 固定右侧边界
            right = mid;
        } else if (nums[mid] < target) {
            // 此时搜索区间为[mid + 1, right)
            left = mid + 1;
        } else if (nums[mid] > target) {
            // 此时搜索区间为 [left ,mid)
            right = mid; 
        }
    }
    // 检查出界情况
    if (left == nums.length || nums[left] != target) {
	    return -1;
	}
    return left;
}

// 左闭右闭区间,搜索左侧边界

int left_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    // 搜索区间为 [left, right]
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            // 收缩右侧边界
            right = mid - 1;
        } else if (nums[mid] > target) {
            // 搜索区间变为 [left, mid-1]
            right = mid - 1;
        } else if (nums[mid] < target) {
            // 搜索区间变为 [mid+1, right]
            left = mid + 1;
        }
    }
    // 检查出界情况
    if (left >= nums.length || nums[left] != target){
        return -1;
    }
    return left;
}

6、搜索右边界

left 的更新必须是 left = mid + 1,就是说 while 循环结束时,nums[left] 一定不等于 target 了,而 nums[left-1] 可能是 target

// 左闭右开区间,搜索右侧边界
int right_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0, right = nums.length;
    while (left < right) {
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            // 收缩左侧边界
            left = mid + 1;
        } else if (nums[mid] < target) {
            // 此时搜索区间为[mid + 1, right)
            left = mid + 1;
        } else if (nums[mid] > target) {
            // 此时搜索区间为 [left ,mid)
            right = mid; 
        }
    }
    // 检查 left 越界的情况
    if (left == 0 || nums[left - 1] != target) {
        return -1;
    }
    return left - 1;
}


// 左闭右闭区间,搜索右侧边界
int right_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            // 收缩左侧边界
            left = mid + 1;
        } else if (nums[mid] > target) {
            // 搜索区间变为 [left, mid-1]
            right = mid - 1;
        } else if (nums[mid] < target) {
            // 搜索区间变为 [mid+1, right]
            left = mid + 1;
        }
    }
    // 检查 right 越界的情况
    if (right < 0 || nums[right] != target){
        return -1;
    }
    return right; // 终止条件为 left == right + 1
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值