二分查找本身并不难, 难的是二分查找的扩展问题
1. 假如 target 不在数组中, 输入其应该在的位置
2. 找到大于 target 的最小值的位置或小于 target 的最大值的位置
3. target 在数组中连续出现多次, 分别找到 target 的最左位置和最右位置
int _bsearch(int A[], int m, int n, int target) {
if(m > n) ----------------------------- 变量 x
return m; ----------------------------- 变量 y
int mid = (m+n) >> 1;
if(A[mid] == target) {
code here ----------------------------- 变量 z
}
}
在二分查找的框架内, 大致有 3 个可变的位置, 分别是 x, y, z 处
具体来说
1. x 可以填写 m >=n 或 m > n
2. y 可以填写 return m, return n, return min(n,m)...
3. z 可以填写 b_search(A, mid+1, n, target) or b_search(A, m, mid-1, target)
一般来讲,
x 处都要填 m > n, 因为当 m==n 时, 代码还需要向下走进行验证
y 处是最难把握的了, 需要具体情况具体分析
z 的话需要考虑 当 a[mid] == target 时, 我们有怎样的需求, 再做选择
例题
1. Leetcode Search in Rotated Sorted Array
class Solution {
public:
int search(int A[], int n, int target) {
if(n < 1)
return -1;
int pivot = A[0];
int pivot_pos = find_pos(A, 0, n-1, pivot);
//cout << "pivot at " << pivot_pos << endl;
int got;
if(pivot == target) {
return 0;
}else if(pivot < target) {
got = b_search(A, 0, pivot_pos-1, target);
if(got < 0 || got >= pivot_pos)
return -1;
return got;
}else{
got = b_search(A, pivot_pos, n-1, target);
if(got < pivot_pos || got >= n)
return -1;
return got;
}
}
int find_pos(int A[], int m, int n, int pivot) {
if(m > n)
return m;
int mid = (m+n)>>1;
//cout << "mid " << mid << endl;
if(A[mid] >= pivot) {
return find_pos(A, mid+1, n, pivot);
}else {
return find_pos(A, m, mid-1, pivot);
}
}
int b_search(int A[], int m, int n, int pivot) {
if(m > n) {
return -1;
}
int mid = (m+n)>>1;
if(A[mid] == pivot) {
return mid;
}else if(A[mid] < pivot) {
return b_search(A, mid+1, n, pivot);
}else{
return b_search(A, m, mid-1, pivot);
}
}
};
2. Leetcode Search Insert Position
y 处. 当 m > n 时, 说明有两种情况发生了, 第一, 上一步是 b_search(A, mid+1, n, target) 这说明, 待求位置在 mid "右边"(A[mid] < target), 所以返回 m (mid+1); 第二种, 上一步是 b_search(A, m, mid-1, target), 说明 (A[mid] > target) 且这是最后一步递归(以发生越界 m>n), 所以我们要插入的位置在mid-1右边, 仍然填 m
#include <iostream>
using namespace std;
class Solution {
public:
int searchInsert(int A[], int n, int target) {
int pos = b_search(A, 0, n-1, target);
return pos;
}
int b_search(int A[], int m, int n, int target) {
if(m > n)
return m;
int mid = (m+n)>>1;
//cout << mid << endl;
if(A[mid] == target) {
return mid;
}else if(A[mid] > target) {
return b_search(A, m, mid-1, target);
}else{
return b_search(A, mid+1, n, target);
}
}
};
3. Leetcode Search for a Range
x 处. m==n 时, 仍需要判断, 所以向下走
z 处. 求 left_position 时, 当 A[mid] == target 时, 我们希望看看 mid 左边是否还有更合适的解, 所以应该 b_search(A, m, mid-1, target) 强制向左, 求 right_position 同理
y 处. 求 left_position 时, 我们总是强制向左, 所以最终会越界, left_position 应该是越界的右端, 返回 m
class Solution {
public:
vector<int> searchRange(int A[], int n, int target) {
int left = left_pos(A, 0, n-1, target);
int right = right_pos(A, 0, n-1, target);
if(left < 0 || left >= n || A[left] != target) {
left = right = -1;
}
vector<int> res;
res.push_back(left);
res.push_back(right);
return res;
}
int left_pos(int A[], int m, int n, int target) {
if(m > n)
return m;
int mid = (m+n)>>1;
// when a[mid] == target, prefer left one
if(A[mid] >= target) {
return left_pos(A, m, mid-1, target);
}else{
return left_pos(A, mid+1, n, target);
}
}
int right_pos(int A[], int m, int n, int target) {
if(m > n)
return n;
int mid = (m+n)>>1;
//cout << mid << endl;
// when a[mid] == target, prefer right one
if(A[mid] > target) {
return right_pos(A, m, mid-1, target);
}else{
return right_pos(A, mid+1, n, target);
}
}
};