算法思想-二分查找
二分查找应用场景:寻找一个数、寻找满足条件的某个区间的左侧边界、寻找满足条件的某个区间的右侧边界
建议学习:二分查找详解
二分查找的基本框架
int binarySearch(vector<int> nums, int target){
int lo=..., hi=...; // 初始化:搜索的边界
while(...){ // 终止条件:区间为空时则停止搜索
int mid = lo + (hi - lo) / 2;
if(nums[mid] == target) // 找到目标
...;
else if(nums[mid] > target) // 更新搜索区间
hi = ;
else if(nums[mid] < target)
lo = ;
}
return ...;
}
在递增数组中寻找一个数(找出index,使得nums[index] == target)
int binarySearch(vector<int> nums, int target){
int lo=0, hi=nums.size()-1; // 搜索闭区间[0:nums.size()-1]
while(lo <= hi){ // 终止条件:区间为空时则停止搜索,lo>hi时区间为空
int mid = lo + (hi - lo) / 2;
if(nums[mid] == target) // 找到目标
return mid;
else if(nums[mid] > target) // 中间值大于目标值,则说明目标值可能在[mid+1: end]中,每一步更新区间都必须缩小区间大小,否则会死循环
hi = mid - 1;
else if(nums[mid] < target) // 中间值小于目标值,则说明目标值可能在[start: mid-1]中,每一步更新区间都必须缩小区间大小,否则会死循环
lo = mid + 1;
}
return -1;
}
在递增数组中寻找满足条件的的区间的左侧边界(找出最小的index使得nums[index] >= target)
int binarySearch(vector<int> nums, int target){
int lo=0, hi=nums.size()-1; // 搜索闭区间[0:nums.size()-1]
while(lo <= hi){ // 终止条件:区间为空时则停止搜索,lo>hi时区间为空
int mid = lo + (hi - lo) / 2;
if(nums[mid] >= target) // 满足条件,说明index在[lo: mid]间,这里之所以还是会mid-1是为了缩小区间
hi = mid - 1;
else if(nums[mid] < target) // 不满足条件,说明index在[mid+1: hi]之间
lo = mid + 1;
}
// 如果数组中不存在>=target的数,hi不会更新,lo会一直更新直到超过nums.size()-1(hi的初始值)
// 如果数组中存在>target的数,nums[lo]一定满足>=target
if(lo >= nums.size())
return -1;
return lo;
}
在递增数组中寻找满足条件的区间的右侧边界(找出最大的index使得nums[index] <= target)
int binarySearch(vector<int> nums, int target){
int lo=0, hi=nums.size()-1; // 搜索闭区间[0:nums.size()-1]
while(lo <= hi){ // 终止条件:区间为空时则停止搜索,lo>hi时区间为空
int mid = lo + (hi - lo) / 2;
if(nums[mid] <= target) // 满足条件,说明index在[mid: hi]间,这里之所以还是会mid+1是为了缩小区间
lo = mid + 1;
else if(nums[mid] > target) // 不满足条件,说明index在[lo: mid-1]之间
hi = mid - 1;
}
// 如果数组中不存在<=target的数,lo不会更新,hi会一直更新直到小于0(lo的初始值)
// 如果数组中存在>target的数,nums[hi]一定满足<=target
if(hi < 0)
return -1;
return hi;
}
二分查找相关题目:
- 求开方 69. Sqrt(x)(Easy)
- 大于给定元素的最小元素744. Find Smallest Letter Greater Than Target(Easy)
- 有序数组的 Single Element 540. Single Element in a Sorted Array(Medium)
- 第一个错误的版本 278. First Bad Version(Easy)
- 旋转数组的最小数字153. Find Minimum in Rotated Sorted Array(Medium)
- 查找区间 34. Find First and Last Position of Element in Sorted Array(Medium)
求开方
求满足条件y*y<x的最大的x
class Solution {
public:
int mySqrt(int x) {
if(x < 2)
return x;
long long lo = 0, hi = x;
while(lo <= hi){
long long mid = lo + (hi - lo) / 2;
if(mid * mid <= x)
lo = mid + 1;
else if(mid * mid > x)
hi = mid - 1;
}
return hi;
}
};
大于给定元素的最小元素
class Solution {
public:
char nextGreatestLetter(vector<char>& letters, char target) {
int lo = 0, hi = letters.size()-1;
while(lo <= hi){
int mid = (lo + hi) / 2;
if(letters[mid] > target)
hi = mid - 1;
else if(letters[mid] <= target)
lo = mid + 1;
}
if(lo == letters.size())
return letters[0];
return letters[lo];
}
};
有序数组的 Single Element
二分搜索,通过两个数中的第一个数的index的奇偶判断single element在这个数的哪一边
class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
int lo = 0, hi = nums.size()-1;
if(nums.size() == 1)
return nums[0];
while(lo <= hi){
int mid = (lo + hi) / 2;
if((mid == 0 && nums[mid] != nums[mid+1]) || (mid == nums.size()-1 && nums[mid] != nums[mid-1]) || (nums[mid-1] != nums[mid] && nums[mid+1] != nums[mid]))
return nums[mid];
if(mid > 0 && nums[mid-1] == nums[mid])
mid -= 1;
if(mid % 2 == 0)
lo = mid + 2;
else
hi = mid - 1;
}
return lo;
}
};
第一个错误的版本
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);
class Solution {
public:
int firstBadVersion(int n) {
if(n == 1)
return 1;
int lo = 1, hi = n;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(isBadVersion(mid))
hi = mid - 1;
else
lo = mid + 1;
}
if(lo > n)
return n;
if(hi < 1)
return 1;
return lo;
}
};
旋转数组的最小数字
class Solution {
public:
int findMin(vector<int>& nums) {
return findMinHelper(nums, 0, nums.size()-1);
}
int findMinHelper(vector<int>& nums, int left, int right){
if(right == left)
return nums[left];
if(right == left + 1)
return min(nums[left], nums[right]);
if(nums[left] < nums[right])
return nums[left];
int mid = (left + right) / 2;
return min(findMinHelper(nums, left, mid), findMinHelper(nums, mid, right));
}
};
查找区间
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> ans(2, -1);
if(nums.size() == 0)
return ans;
int lo, hi;
lo = 0;
hi = nums.size() - 1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] >= target)
hi = mid - 1;
else if(nums[mid] < target)
lo = mid + 1;
}
if(lo != nums.size() && nums[lo] == target)
ans[0] = lo;
lo = 0;
hi = nums.size() - 1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] > target)
hi = mid - 1;
else if(nums[mid] <= target)
lo = mid + 1;
}
if(hi != -1 && nums[hi] == target)
ans[1] = hi;
return ans;
}
};