二分查找 边界查找


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. 二分查找的应用

力扣153. 寻找旋转排序数组中的最小值

/*
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

力扣34

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值