二分查找的注意点:
- 区间选择(与哪些值比较
target/nums[right-1]/nums[mid-1]
) - 中位数(左中位数 or右中位数)。 for case [3,1] 能正确二分。
- while终止条件:要保证区间选择的合法性,比如
mid-1 in [left, right)
。
二分查找范围
左闭右开
class Solution {
public:
int findleft(vector<int>& nums, int target) {
int left = 0, right=nums.size();
while(left<right) {
int mid = left + (right-left)/2;
int m = nums[mid];
if(m<target){ left = mid+1;
} else if(m>target) { right = mid;
} else if(m==target) { right = mid; }
}
if(left<nums.size()&&nums[left]==target) return left;
else return -1;
}
int findright(vector<int>& nums, int target) {
int left = 0, right=nums.size();
while(left<right) {
int mid = left + (right-left)/2;
int m = nums[mid];
if(m<target){ left = mid+1;
} else if(m>target) { right = mid;
// } else if(m==target) { right = mid; }
} else if(m==target) { left = mid+1; }
}
// if(left<n&&nums[left]==target) return left;
if(nums[right-1]==target) return right-1;
else return -1;
}
vector<int> searchRange(vector<int>& nums, int target) {
int l = findleft(nums, target);
if (l==-1) return {-1, -1};
else return {l, findright(nums, target)};
}
};
左闭右闭
待补充
旋转数组
常见用例:[4,5,1,2,3]/[1,2]/[2]
因为区间选择时要与nums[right-1]
比较,所以中位数应为左中位数。
- 区间选择:
nums[right-1]
- 中位数:左中位数。左中位数 使得
left != right
for base case [2,3] right--
减而治之。收缩右边界。for case [2,2,2,2]
旋转数组的查找值II
class Solution {
public:
bool search(vector<int>& nums, int target) {
int left = 0, right=nums.size();
while(left<right) {
int mid = left + (right-left)/2;
int m = nums[mid], l = nums[left], r = nums[right-1];
if (m==target) return true;
if(m<r) {
if(m<target&&target<=r) left = mid+1;
else right = mid;
} else if(m>r) {
if(l<=target&&target<m) right = mid;
else left = mid+1;
} else if(m==r) {
right--;
}
}
return false;
}
};
旋转数组的最小值II
class Solution {
public:
int findMin(vector<int>& nums) {
int left= 0, right=nums.size();
while(left<right) {
int mid = left+(right-1-left)/2;
int l=nums[left], m=nums[mid], r=nums[right-1];
if(m<r) right = mid+1;
else if(m>r) left = mid+1;
else if(m==r) right--;
}
return nums[left];
}
};
寻找峰值
- 区间选择:比较
nums[mid-1] ?= nums[mid]
- 结束条件:
left<right-1
。 保证mid-1有效for base case [2] 。 而且峰值必然存在,所以最终结果的[left,right)有且仅有一个元素。
常见用例:[1,2], [1,2,1,2]
class Solution {
public:
int findMin(vector<int>& nums) {
int left= 0, right=nums.size();
while(left<right) {
int mid = left+(right-1-left)/2;
int l=nums[left], m=nums[mid], r=nums[right-1];
if(m<r) right = mid+1;
else if(m>r) left = mid+1;
else if(m==r) right--;
}
return nums[left];
}
};
二维数组查找
- 中位数的选择:
nums[x][y] ?= target
- 区间选择:
x++, y--
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
int nx = array.size();
int ny = array[0].size();
int x=0, y=ny-1;
while(x<nx && y>=0) {
int val = array[x][y];
if(val<target) { x++;
} else if(val>target) { y--;
} else if(val==target) { return true; }
}
return false;
}
};
K个最接近的数
- 基本思路:二分法 + 滑动窗口
[nums[mid],nums[mid+k])
- 区间选择:
x-nums[mid] ?= nums[mid+k]
class Solution {
public:
vector<int> findClosestElements(vector<int>& arr, int k, int x) {
vector<int>& nums = arr;
vector<int> res;
int left=0, right=nums.size()-k;
while(left<right) {
int mid = left+(right-left) /2;
if(x-nums[mid]>nums[mid+k]-x) { left = mid+1;
} else { right = mid; }
}
vector<int> res(nums.begin()+left, nums.begin()+left+k);
return res;
}
};