1. 基本原理
注意点
- 初始条件
- 终止条件
- 上下区间更新方法
- 返回值选择
1.1 循环实现
/**
* @brief 循环实现二分查找
* @param nums 数据
* @param target 查找目标
* @return 找到目标则返回下标,否则返回-1
*/
int BSearch(const vector<int> &nums, int target) {
int low = 0;
int high = nums.size() - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);//这种写法不容易溢出且更加高效
if (nums[mid] == target) return mid;
else if (nums[mid] > target) high = mid - 1;
else low = mid + 1;
}
return -1; //找不到
}
时间复杂度O(logn),空间复杂度O(1)
1.2 递归实现
int BSearchRecursion(const vector<int> &nums, int low, int high, int target) {
if (low > high) return -1;
int mid = low + ((high - low) >> 1);
if (nums[mid] == target) return mid;
else if (nums[mid] > target) return BSearchRecursion(nums, low, mid-1, target);
else return BSearchRecursion(nums, mid+1, high, target);
}
时间复杂度O(logn),空间复杂度O(logn)
2. 简单应用
2.1 x的平方根
求x的平方根,要求精确到小数点后6位
double MySqrt(double x) {
assert(x > 0);
double low = 0;
double high = x;
double p = 1e-6;
while (1) {
double mid = low + (high - low) / 2;
if ((mid+p)*(mid+p) > x && (mid-p)*(mid-p) < x) return mid;
if (mid * mid > x) high = mid - p;
else low = mid + p;
}
}
class Solution {
public:
int mySqrt(int x) {
int low = 1;
int high = x;
while (1) {
int mid = low + ((high - low) >> 1);
if ((long long)mid*mid <= x && (long long)(mid+1)*(mid+1) > x) return mid;
else if ((long long)mid*mid > x) high = mid - 1;
else low = mid + 1;
}
}
};
时间复杂度O(logn),空间复杂度O(1)
3. 变形应用
3.1 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
// 1.查找第一个位置
vector<int> ans{-1, -1};
int low = 0;
int high = nums.size() - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (nums[mid] == target) {
if (mid == 0 || nums[mid-1] != target) { //此时左边没有等于nums[mid]的元素
ans[0] = mid;
break;
}
else {
high = mid - 1;
}
}
else if (nums[mid] > target) high = mid - 1;
else low = mid + 1;
}
// 没找到
if (-1 == ans[0]) return ans;
// 2.查找最后一个位置
low = ans[0]; //加快搜索
high = nums.size() - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (nums[mid] == target) {
if (mid == nums.size() - 1 || nums[mid+1] != target) { //此时右边没有等于nums[mid]的元素
ans[1] = mid;
break;
}
else {
low = mid + 1;
}
}
else if (nums[mid] > target) high = mid - 1;
else low = mid + 1;
}
return ans;
}
};
时间复杂度log(n),空间复杂度O(1)
3.2 查找第一个大于等于给定值的元素
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
// 找出第一个大于等于target目标的数,若没有返回尾后位置
int low = 0;
int high = nums.size() - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (nums[mid] < target) {
low = mid + 1;
}
else if (mid == 0 || nums[mid-1] < target) { //若成立则是第一个大于等于target的元素
return mid;
}
else {
high = mid - 1;
}
}
return nums.size(); // 不存在大于等于target的数,返回尾后位置
}
};
3.3 循环序列中的二分查找问题
关键在于找到特例情况 ,写法不一定最简洁, 但是容易理解
class Solution {
public:
int search(vector<int>& nums, int target) {
int low = 0;
int high = nums.size() - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (nums[mid] == target) return mid;
else if (nums[mid] < target) {
if (nums[mid] <= nums[high] && nums[high] <= nums[low] && nums[low] <= target){ //特例
high = mid - 1;
}
else {
low = mid + 1;
}
}
else {
if (target <= nums[high] && nums[high] <= nums[low] && nums[low] <= nums[mid]) { //特例
low = mid + 1;
}
else {
high = mid - 1;
}
}
}
return -1; //找不到
}
};
时间复杂度O(logn),空间复杂度O(1)