基本思想
二分搜索是基于分治的算法,该算法的基本思想为每次都把数据分成左右两部分,一部分含有要查找的数据,另一部分不包含要查找的数据。
最常见的使用场景为:在排序的数组中查找特定元素的索引。
- 最差复杂性: O(log n)
- 平均复杂性: O(log n)
- 最优复杂性: O(1)
- 空间复杂性: O(1)
常规二分搜索
此处有几处需要注意的事项:
-
while循环的等号不可以省略,是防止当数据仅有一个时,例如
nums = {1}; target = 1能够正确找到对应的索引; -
如果要查找的数据中有多个target,该方法只会找到其中任意的一个target,视数据情况而定,如需要特定查找左边或者右边可以使用下边的变形二分查找。
-
计算
mid时,left + right可能会超过int的最大值,此处可以采用left + (right - left) / 2防止越界,因为:left + (right - left) / 2 = left - left / 2 + right / 2 = (left + right) / 2 -
如所给数据中不包含
target,返回的结果为所给数据中应在的位置的索引。
int binary_search(int nums[], int target, int left, int right) {
while (left <= right) {
int mid = (left + right) >> 1; // 右移一位相当于除以2
if (nums[mid] < target) left = mid + 1; // 要查找的数据在mid的右边
else if (nums[mid] > target) right = mid - 1; // 要查找的数据在mid的左边
else return mid; // 找到了改元素,返回索引
}
return -1;
}
力扣题目
变形二分搜索
左右二分查找
在常规的二分查找中,如果有多个target,查找结果与所给数据相关。因此,如果我们想要target中最左边或者最右边的索引,即可使用下边的变形二分查找。当left = right时,结束循环。
查找最右边元素的索引
找到小于等于 target 的最大的元素的索引。
注意 :
此处计算mid时需要加1,如果不加1,当left = right - 1时,除法为向下取整,mid = (left + right) / 2 = left,如nums[mid] <= target成立,下次mid仍然left,则会造成死循环
int right(int nums[], int target, int left, int right) {
while (left < right) {
int mid = (left + right + 1) >> 1;
if (nums[mid] <= target) left = mid;
else right = mid - 1;
}
return left;
}
查找最左边元素的索引
找到大于等于 target 的最小的元素的索引。
int left(int nums[], int target, int left, int right) {
while (left < right) {
int mid = (left + right) >> 1;
if (nums[mid] >= target) right = mid;
else left = mid + 1;
}
return left;
}
上边两个查找都可以去掉等号,变为找小于 target 的最大元素的索引(大于 target 的最小元素的索引)。
#include <bits/stdc++.h>
using namespace std;
int left(vector<int> &nums, int t, int l, int r) {
while (l < r) {
int mid = (l + r) >> 1;
if (nums[mid] >= t) r = mid;
else l = mid + 1;
}
return l;
}
int right(vector<int> &nums, int t, int l, int r) {
while (l < r) {
int mid = (l + r + 1) >> 1;
if (nums[mid] <= t) l = mid;
else r = mid - 1;
}
return l;
}
int upper(vector<int> &nums, int t, int l, int r) {
while (l < r) {
int mid = (l + r) >> 1;
if (nums[mid] <= t) l = mid + 1;
else r = mid;
}
return l;
}
int main() {
vector<int> nums = {1, 2, 3, 3, 4, 5, 6};
// 此处可以不减一,如不减,当查找7时,结果为size
int size = nums.size() - 1;
cout << left(nums, 7, 0, size) << endl;
cout << right(nums, 7, 0, size) << endl;
// 与STL库中upper_bound效果一致
cout << upper(nums, 7, 0, size) << endl;
cout << lower_bound(nums.begin(), nums.end(), 7) - nums.begin() << endl;
cout << upper_bound(nums.begin(), nums.end(), 7) - nums.begin() << endl;
return 0;
}