二分算法的基础知识
二分查找算法是二分算法的一个子类
二分查找是对顺序查找的一种优化
- 在一个有序查找区间内
- min是头指针,max是尾指针,mid = (min + max) / 2
- 如果arr[mid] < x min =mid + 1
- 如果arr[mid] > x max =mid - 1
- 如果arr[mid] = x 找到结果
- 终止条件:min >= max
二分算法 二分的就是查找区间的范围 n->n/2, 二分的是问题的规模
int binary_search(int *arr, int n, int x){
int head = 0, tail = n - 1, mid;
while(head <= tail){
mid = head + ((tail - head) >> 1);
if(arr[mid] == x) return mid;
if(arr[mid] < x) head = mid + 1;
else tail = mid - 1;
}
return -1;
}
二分查找 - 泛型情况
0 - 1 查找模型 0: 条件不成立, 1:条件成立
前面一堆0 ,后面一堆1,找到第一个1
int binary_search_01(int *arr, int n, int x){
int head = 0, tail = n - 1, mid;
while(head < tail){
mid = head + ((tail - head) >> 1);
if(arr[mid] < x) head = mid + 1;
else tail = mid;
}
return head;
}
经典面试题 - 简单二分应用
69. x 的平方根
- x越大,根号x就越大
- 因此是个单调区间
class Solution {
public:
int mySqrt(int x) {
double head = 0, tail = x, mid;
tail += 1;
for(int i = 0; i < 100; i++){
mid = head + ((tail - head) / 2.0);
if(mid * mid <= x) head = mid;
else tail = mid;
}
return floor(head);
}
};
35. 搜索插入位置
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int head = 0, tail = nums.size() - 1, mid;
while(tail - head > 3){
mid = (head + tail) >> 1;
if(nums[mid] >= target) tail = mid;
else head = mid + 1;
}
for(int i = head; i <= tail; i++){
if(nums[i] >= target) return i;
}
return nums.size();
}
};
1. 两数之和
- 套娃: 下标数组,通过下标数组对数组进行排序
- 选中某个值val,二分查找剩下一个值为 target - val
class Solution {
public:
int binary_search(vector<int> &nums, vector<int> &ind, int head, int x){
int tail = ind.size() - 1, mid;
while(head <= tail){
mid = (head + tail) >> 1;
if(nums[ind[mid]] == x) return mid;
if(nums[ind[mid]] < x) head = mid + 1;
else tail = mid - 1;
}
return -1;
}
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ind(nums.size());
for(int i = 0; i < ind.size(); i++) ind[i] = i;
sort(ind.begin(), ind.end(), [&](int i, int j){ return nums[i] < nums[j];});
vector<int> ret(2);
for(int i = 0; i < ind.size(); i++){
int val = nums[ind[i]];
int j = binary_search(nums, ind, i + 1, target - val);
if(j == -1) continue;
int a = ind[i];
int b = ind[j];
if(a > b) swap(a, b);
ret[0] = a;
ret[1] = b;
}
return ret;
}
};
34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
int binary_search(vector<int> &nums, int x){
int head = 0, tail = nums.size() - 1, mid;
while(tail - head > 3){
mid = (head + tail) >> 1;
if(nums[mid] >= x) tail = mid;
else head = mid + 1;
}
for(int i = head; i <= tail; i++){
if(nums[i] >= x) return i;
}
return nums.size();
}
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> ret(2);
ret[0] = ret[1] = -1;
ret[0] = binary_search(nums, target);
if(ret[0] == nums.size() || nums[ret[0]] != target){
ret[0] = ret[1] = -1;
return ret;
}
ret[1] = binary_search(nums, target + 1) - 1;
return ret;
}
};
1658. 将 x 减到 0 的最小操作数
class Solution {
public:
int binary_search(vector<int> &nums, int x){
int head = 0, tail = nums.size() - 1, mid;
while(head <= tail){
mid = (head + tail) >> 1;
if(nums[mid] == x) return mid;
if(nums[mid] < x) head = mid + 1;
else tail = mid -1;
}
return -1;
}
int minOperations(vector<int>& nums, int x) {
vector<int> suml(nums.size() + 1);
vector<int> sumr(nums.size() + 1);
suml[0] = sumr[0] = 0;
for(int i = 0; i < nums.size(); i++) suml[i + 1] = suml[i] + nums[i];
for(int i = nums.size() - 1; i >= 0; --i) sumr[nums.size() - i] = sumr[nums.size() - i - 1] + nums[i];
int ans = -1;
for(int i = 0; i < suml.size(); i++){
int j = binary_search(sumr, x - suml[i]);
if(j == -1) continue;
if(i + j > nums.size()) continue;
if(ans == -1 || ans > i + j) ans = i + j;
}
return ans;
}
};
475. 供暖器
class Solution {
public:
int binary_search_01(vector<int> &nums, int x){
int head = 0, tail = nums.size() - 1, mid;
while(head < tail){
mid = (head + tail) >> 1;
if(nums[mid] >= x) tail = mid;
else head = mid + 1;
}
return head;
}
int findRadius(vector<int>& houses, vector<int>& heaters) {
sort(heaters.begin(), heaters.end());
int ans = 0;
for(auto x: houses){
int j = binary_search_01(heaters, x);
int a = abs(heaters[j] - x);
int b = (j ? x - heaters[j-1] : a + 1);
ans = max(ans, min(a, b));
}
return ans;
}
};
81. 搜索旋转排序数组 II
class Solution {
public:
bool search(vector<int>& nums, int target) {
if(nums[0] == target || nums[nums.size() - 1] == target) return true;
int l = 0, r = nums.size() - 1, mid, head, tail;
while(l < nums.size() && nums[l] == nums[0]) ++l;
while(r >= 0 && nums[r] == nums[0]) --r;
while(l <= r){
mid = (l + r) >>1;
if(nums[mid] == target) return true;
if(nums[mid] <= nums[tail]){
if(target <= nums[tail] && target > nums[mid]) l = mid + 1;
else r = mid - 1;
}else{
if(target < nums[mid] && target >= nums[head]) r = mid - 1;
else l = mid + 1;
}
}
return false;
}
};