2021/4/19更新
今天在小组讲了二分,突然感觉有种二分很简单的错觉,copy一下labuladong总结的模板:
第一种:最简单的二分查找
因为我们初始化 right = nums.length - 1
所以决定了我们的「搜索区间」是 [left, right]
所以决定了 while (left <= right)
同时也决定了 left = mid+1 和 right = mid-1
因为我们只需找到⼀个 target 的索引即可
所以当 nums[mid] == target 时可以⽴即返回
第二种:寻找左边界
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要⽴即返回
⽽要收紧右侧边界以锁定左侧边界
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最右侧索引
所以当 nums[mid] == target 时不要⽴即返回
⽽要收紧左侧边界以锁定右侧边界
⼜因为收紧左侧边界时必须 left = mid + 1
所以最后⽆论返回 left 还是 right,必须减⼀
至于代码,在leetcode704, 34中看吧。
另外,今天发现了C的标准库中有bsearch可以用。(感觉还不如自己写一个,懒得记23333)
int cmpfunc(const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
int search(int* nums, int numsSize, int target){
int *item= (int*) bsearch(&target, nums, numsSize, sizeof(int),cmpfunc);
if (item!=NULL)
return item-nums;
else
return -1;
}
二分搜索不难,但总是会有些“等号”“+1”“-1”的问题想的人抓耳挠腮
但是不要怕!二分真的不难!
写代码之前一定要打草稿!先写好伪码(一定不能怕麻烦边写代码边想)!
然后再考虑全测试用例!
考虑:
- 空
- 只有一个元素
- 二分时奇数偶数两种情况
- target在左/右边界,target不存在
想练手?安排。
23. Search in Rotated Sorted Array
链接: https://leetcode.com/problems/search-in-rotated-sorted-array/.
·
先
·
想
·
再
·
看
·
至少得先写伪码才能看!
不自觉警告⚠️
·
class Solution {
public:
int search(vector<int>& nums, int target) {
vector<int>::size_type N=nums.size();
int l=0;
int r=N-1;
int m;
while (l<=r){ //⚠️
m=(l+r)/2;
if (nums[m]==target)
return m;
else if (nums[l]<=nums[m]){ //⚠️
if (nums[l]<=target && target<=nums[m]) //⚠️
r=m-1;
else
l=m+1;
}
else{
if (nums[m]<=target && target<=nums[r]) //⚠️
l=m+1;
else
r=m-1;
}
}
return -1;
}
};
参考: https://blog.csdn.net/fuxuemingzhu/article/details/79534213.
34. Find First and Last Position of Element in Sorted Array
emmmmm这题,我先是想在一次二分中同时找上下bound,找一个是没问题,但再遍历找边界岂不是O(n)了咋都想不出来,一查就给跪了, 可以两次二分,分别找上下bound,合起来是O(logn)+O(logn),不还是O(logn)吗,哭泣。
c++有3个二分查找的函数:
lower_bound(起始地址,结束地址,要查找的数值) 返回的是数值 第一个 出现的位置。
upper_bound(起始地址,结束地址,要查找的数值) 返回的是 第一个大于待查找数值 出现的位置。
binary_search(起始地址,结束地址,要查找的数值) 返回的是是否存在这么一个数,是一个bool值。
注意:使用二分查找的前提是数组有序。函数返回值是vector::iterator
然鹅,upper_bound的返回值很容易出现越界的情况,搞不定的我决定还是自己写函数,越界的话high就为N.
菜鸟不易,自己写函数又有一堆问题,整了半天还是各种有问题,最后发现还是伪码写的不对,主要还是停下来跳出循环的问题,刚开始还行写着写着忽略了target不存在的问题。
思维还是很乱。下面直接用大佬的了,多学学吧。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int low = lower_index(nums, target);
int high = upper_index(nums, target);
if (low == high) return {-1, -1};
return {low, high-1};
}
/* // BF O(n)
int lower_index(vector<int>& nums,int target){
// return the index of the first number which is not less than target
vector<int>::size_type N=nums.size();
for (int i=0;i<N;++i){
if (nums[i]>=target)
return i;
}
return int(N);
}
int upper_index(vector<int>& nums,int target){
// return the index of the first number which is greater than target
vector<int>::size_type N=nums.size();
for (int i=0;i<N;++i){
if (nums[i]>target)
return i;
}
return int(N);
}
*/
// binary search O(logn)
int lower_index(vector<int>& nums, int target) {
const int N = nums.size();
// [l, r) ⚠️
int l = 0, r = N; //⚠️
while (l < r) { //⚠️
int mid = l + (r - l) / 2;
if (nums[mid] >= target) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
int upper_index(vector<int>& nums, int target) {
const int N = nums.size();
// [l, r)
int l = 0, r = N;
while (l < r) {
int mid = l + (r - l) / 2;
if (nums[mid] <= target) {
l = mid + 1;
} else {
r = mid;
}
}
return l;
}
};