1. 简介
二分查找是一种高效的查找算法,二分必有序,二分查找有许多细节
2. 左右指针初始化
l = -1, r = N; 指针不越界, 是一个左开右开区间 (l,r); 当l + 1 = r时, 区间为空,不包含任何元素
考虑两种极端情况
情况1: arr = [1,2,3,4,5,6], key = 7, 小于时l指针不断右移
初始 l = -1, r = N 当 l = N-2, N-2+1 = N-1, 当 l = N-1,N-1+1 = N = r, 退出while, 左指针不越界 情况2: arr = [1, 2, 3, 4, 5, 6], key = 0, 大于时r指针不断左移
初始 l = -1, r = N 当 r = 1, -1+1+1 = 1 = r, 退出while, 右指针不越界
判断条件 while l+1 != r
l min = -1, r min = 1, 此时,m = (l+r)/2 = 0,
l max = N-2, r max = N, 此时 m = (l+r)/2 = N-1
m 属于[0,N)
3. 整数加法转换成减法,保证整数不溢出
l ∈ [-1,N-1)
r∈ [0,N]
m = l + (r-l)/2,
m ∈[0,N)
4. 判断条件 和返回值
例如, arr = [1,2,3,4,5,5,5,8,9]
#第一个大于 key = 5, 即 num = 8, arr[m] <= key, return arr[r]
#第一个大于等于key = 5 , 即 num = 5, arr[m] < key, return arr[r]
#最后一个小于 key = 5, 即 num = 4 , arr[m] < key,return arr[l]
#最后一个小于等于key = 5, 即 num = 5, arr[m] <= key, return arr[l]
总结就是,小于一定会返回l, 大于一定返回r; 其中,判断条件,严格小于 <, 严格大于 <=
图示:
5. python 实现
def binary_search(arr,key):
N = len(arr)
l, r = -1, N #1
while l+1 != r: #2
m = int(l + (r-l)/2) #3
if arr[m] <= key: #4
l = m #5
else:
r = m
return arr[r] #6
if __name__ == '__main__':
arr = [1,2,3,4,5,5,5,8,9]
key = 5
num = binary_search(arr,key)
print(num)
2023.1.16更新
查找下边界,lowerbound, 有三种写法
#include<iostream>
#include<vector>
using namespace std;
/*
lowerboud [1,2,3,4,7,8,8,9] ,query = 8, 返回第一个8的下标5
第一种,左闭右闭区间 [left,right]
初始化 left = 0, right = n-1
第二种,将区间的左端点或者右端点初始化成开区间 [left,right), 或者(left,right]
初始化 left = 0, right = n
第三种,左开右开区间 (left,right)
left = -1, right = n
*/
int binary_search1(vector<int> &nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) { //区间不为空
int mid = left + (right - left ) / 2;
if (nums[mid] < target) {
left = mid + 1; //[mid+1,right]
} else {
right = mid - 1; //[left, mid-1]
}
}
return left;
}
int binary_search2(vector<int> &nums, int target) {
int left = 0, right = nums.size(); //左闭右开区间[left,right)
while (left < right) { //区间不为空
int mid = left + (right - left ) / 2;
if (nums[mid] < target) {
left = mid + 1; //[mid+1,right)
} else {
right = mid; //[left, mid)
}
}
return left; //或者return right, 因为while退出时left = right
}
int binary_search3(vector<int> &nums, int target) {
int left = -1, right = nums.size(); //左开右开区间(left,right)
while (left + 1 < right) { //区间不为空
int mid = left + (right - left ) / 2;
if (nums[mid] < target) {
left = mid; //(mid,right)
} else {
right = mid; //(left, mid)
}
}
return right; //或者return right, 因为while退出时left = right
}
int main() {
vector<int> nums = {1,2,3,4,7,8,8,9};
int target = 8;
cout <<binary_search1(nums,target) << endl;
cout <<binary_search2(nums,target) << endl;
cout <<binary_search3(nums,target) << endl;
return 0;
}
6. 二分查找的应用
/*
5,4,3,2,1,6,7
如果nums[mid] < nums[high] 说明 nums[mid]是最小值右侧的元素
如果nums[mid] > nums[high] 说明 nums[mid]是最小值左侧的元素
*/
class Solution {
public:
int findMin(vector<int>& nums) {
int n = nums.size();
//在[0,n-2]中进行二分
int left = 0, right = n-1; //左闭右开区间 [0,n-1)
while (left < right) // left = right时区间为空
{
int mid = left + (right-left) / 2;
if (nums[mid] < nums[right]) {
right = mid;
} else {
left = mid + 1;
}
}
return nums[left];
}
};
2021.11.18更新
力扣34 34. 在排序数组中查找元素的第一个和最后一个位置
#include<bits/stdc++.h>
using namespace std;
/*
cpp lambda 函数
[] (int x, int y) { return x + y; } // 隐式返回类型
[] (int& x) { ++x; } // 没有 return 语句 -> Lambda 函数的返回类型是 'void'
[] () { ++global_x; } // 没有参数,仅访问某个全局变量
[] { ++global_x; } // 与上一个相同,省略了 (操作符重载函数参数)
*/
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if(nums.size() == 0){
return {-1,-1};
}
if(nums.size() == 1){
return nums[0] == target ? vector<int>(2,0) : vector<int>(2,-1);
}
//找开始位置
auto lambda_func = [](int x,int y,bool lower_bound){return lower_bound ? x < y : x <= y;};
int lower_bound = binary_search(nums,target,lambda_func,true);
//找结束位置
int upper_bound = binary_search(nums,target,lambda_func,false);
return {lower_bound,upper_bound};
}
int binary_search(vector<int> &nums, int target, bool(*func)(int a,int b,bool lower), bool lower_bound = false){
int l = -1, r = nums.size();
while(l + 1 != r){
int mid = l + (r - l)/2;
if(func(nums[mid],target,lower_bound)){
l = mid;
}else{
r = mid;
}
}
if(lower_bound){
if(r == nums.size()){
return -1;
}else{
return nums[r]==target ? r : -1;
}
}
else{
if(l == -1){
return l;
}else{
return nums[l] == target ? l : -1;
}
}
}
};
ostream& operator<<(ostream &os,vector<int> &nums){
for(int i=0; i < nums.size(); i++){
os << nums[i] << " ";
}
os << endl;
return os;
}
int main(){
vector<int> nums = {2,2};
int target = 3;
Solution s;
vector<int> result = s.searchRange(nums,target);
cout << result;
return 0;
}
/*
测试用例
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
输入:nums = [], target = 0
输出:[-1,-1]
[2,2]
1
[2,2]
3
*/
参考:
bilibili
https://www.bilibili.com/video/BV1AP41137w7/?vd_source=843e98298bc70b0e35331918314486ce