2020年8月7日 周五 天气晴 【不悲叹过去,不荒废现在,不惧怕未来】
最近一直在LeetCode上刷二分查找相关的题,发现有几点细节问题需要注意,特地总结如下:
(1)之所以将 mid = (left + right) / 2 写成 mid = left + (right - left) / 2,是怕 left + right 会整型溢出。
(2)除以2没必要写成 >> 1,写成 /2 就行,因为编译器会自动把 /2 优化成 >> 1,写成左移的形式反而会降低代码的可读性。
(3)如果在二分查找问题中遇到要返回数组下标,并且这个下标可能会越界(比如插入数据到数组尾部,返回的下标就是越界的),这时候可以考虑将左边界 r 改成数组的长度 len ,而不是 len - 1。
(4)二分查找代码的写法不唯一,选择一种自己喜欢的就好,下面附上我自己常用的写法:
// 返回的是 是否存在target 这么一个数
int BinarySearch(vector<int>& nums, int target) {
int l = 0, r = nums.size()-1;
while (l <= r) {
int m = (r + l) / 2;
if (nums[m] == target)
return m;
else if(nums[m] < target)
l = m + 1;
else
r = m - 1;
}
return -1;
}
二分查找有很多变形,比如查找第一个(最后一个)大于(小于或等于)指定元素的位置,都是根据上面的代码改动而来,因此最基本的要牢记,需要变形的时候简单修改一下就可以了。
2021年02月02日 周二
今天在做二分查找变形题目的时候,才发现细节是魔鬼,并不是我之前说的“简单修改一下就可以了”,所以这里还是记录一下关于二分查找的变形,题目可参考剑指 Offer 53 - I. 在排序数组中查找数字 I(二分查找变形,也就是手撸 lower_bound()、upper_bound())。
手撸 lower_bound()、upper_bound() 代码如下:
// 返回 第一个大于或等于target 的元素位置(lower_bound())
int my_lower_bound(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;
while (l < r) {
int m = l + (r - l) / 2;
if (nums[m] < target) l = m + 1;
else if (nums[m] == target) r = m;
else r = m - 1;
}
// 细节:l == r 的情况单独判断,不容易出错
return nums[l] < target ? -1 : l;
}
// 返回 第一个大于target 的元素位置(upper_bound())
int my_upper_bound(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;
while (l < r) {
int m = l + (r - l) / 2;
if (nums[m] < target) l = m + 1;
else if (nums[m] == target) l = m + 1;
else r = m;
}
// 细节:l == r 的情况单独判断,不容易出错
return nums[l] <= target ? -1 : l;
}