目录
二分查找基本模板
关键 :
初始化左闭右开
l<r退出循环
向右排除mid元素 l=mid+1
检查最终位置是否满足预期
<1>使用左闭右开区间
1.初始化时也要左闭右开
2.好处是从而循环条件是l<r ,退出时 l == r,不必纠结返回l还是r
<2>由于左区间为闭区间,因此首先写排除mid元素向右边收缩的逻辑分支
非常适合找到条件为第一个…的问题
如:第一个 == target :
while(l<r)
int mid=(l+r)/2;
if(nums[mid]<nums[l] l=mid+1;
else r=mid
}
return l>=0 && l<nums.size() && target==nums[l] ? l:-1;
第一个>target
第一个≥target
<3>对于向左收缩的问题转换为向右收缩
如:最后一个<target 转换为:找到第一个≥target 位置的左侧位置
最后一个<=target 转化为:找到第一个>target 位置的左侧位置
最后一个 == target 转化为:找到第一个>target位置的左侧位置,并判断该位置是否==target
int BiSearch(int target,vector<int>&nums){
if(nums.empty())return -1;
int l=0,r=nums.size(); // 1.初始化确定左闭右开区间
while(l<r) //区间的左闭右开决定退出条件为l<r。 右侧为不可达到位置
{
int mid = (l+r)>>1; //此处也经常用 l+(r-l)/2 以防止溢出
if(排除mid元素向右收缩)//括号里为排除当前mid元素的条件
l=mid+1;
}else{
r=mid;
}
//退出时 r==l 因此返回l或r均可
检查l是否为有效位置,且nums[l]满足条件
//如 l>=0 && l<nums.size() && nums[i]==target
}
int Equal(int target,vector<int>nums){
if(nums.empty()) return -1;
int l=0,r=nums.size();
while(l<r){
int mid=(l+r)/2;
if(nums[mid]<target){
l=mid+1;
}else if(nums[mid]>target){
r=mid;
}else{
return mid;
}
}
return -1;
}
int FirstEqual(int target,vector<int>nums){
if(nums.empty()) return -1;
int l=0,r=nums.size();
while(l<r){
int mid=(l+r)/2;
if(nums[mid]<target){
l=mid+1;
}else {
r=mid;
}
}
return l<nums.size() && nums[l]==target?l:-1;
}
int FirstGreaterEqual(int target,vector<int>nums){
if(nums.empty()) return -1;
int l=0,r=nums.size();
while(l<r){
int mid=(l+r)>>1;
if(nums[mid]<target){
l=mid+1;
}else{
r=mid;
}
}
return l;
}
int FirstGreater(int target,vector<int>&nums){
int l=0,r=nums.size();
while(l<r){
int mid=(l+r)>>1;
if(nums[mid]<=target){
l=mid+1;
}else{
r=mid;
}
}
return l;
}
int LastEqual(int target,vector<int>&nums){
//namely the first left pos of first greater position;
int l=0,r=nums.size();
while(l<r){
int mid=(l+r)>>1;
if(nums[mid]<=target){
l=mid+1;
}else{
r=mid;
}
}
int pos=l-1;
return pos>=0 && pos<nums.size()&&target==nums[pos]?pos:-1;
}
int LastEqualSamller(int target,vector<int>&nums){
//namely the left position of the first greater position.
int l=0,r=nums.size();
while(l<r){
int mid=(l+r)>>1;
if(nums[mid]<=target){
l=mid+1;
}else{
r=mid;
}
}
return l-1;
}
int LastSmaller(int target,vector<int>&nums){
int l=0,r=nums.size();
while(l<r){
int mid=(l+r)>>1;
if(nums[mid]<target){
l=mid+1;
}else{
r=mid;
}
}
return l-1;
}
基本题型
二维有序矩阵查找!!!
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.empty()) return false;
int m=matrix.size();
int n=matrix[0].size();
//左闭右开区间 模板
int l=0,r=m*n;
while(l<r){
int mid=(l+r)>>1;
if(matrix[mid/n][mid%n]<target){
l=mid+1;
}else{
r=mid;
}
}
return l>=0 && l<m*n && matrix[l/n][l%n]==target?true:false;
}
有序数组中第一个和最后一个目标位置!!!
查找插入位置
二分查找高级应用
两个有序数组的中位数
中位数定义:
对于有序集合C {C0 C1 … Cn-1},其中位数将其均分为左右数量相等(n为偶数)或(左集合数量 == 右集合数量+1 )(n为奇数)
即
中位数将集合均分,均分点为i,则
C{C0 C1 ... Cn-1} == >Cleft: {C0 C1... Ci} | Cright:{Ci+1....Cn-1}
<1>左右数量相等
count(Cleft)=i+1,count(Cright)=n-1-i-1+1=n-i-1
当n为奇数 count(left)==count(right)+1==> i+1 == n-i-1+1
当n为偶数 count(left)==count(right)==> i+1 == n-i-1
<2> max{left}<=max{right}
假设有序集合{C}由有序集合{A} {B} 合并而成,即
C=A +B=Cleft+Cright={Cleft ... Cright}
则有
Aleft=A ∩ Cleft,Aright=A ∩ Cright
同理,
Bleft=B ∩ Cleft,Bright=B ∩ Cright
故
A={Aleft | Aright} ={A0 A1... Ai | Ai+1....Am-1}
B={Bleft | Bright}={B0 B1....Bj | Bj+1 ... Bn-1}
i,j分别为A B集合的左右子集合切分点。
故问题转换为在AB中寻找切分点i,j使得:
左边 | 右边
Cleft | Cright
A0 A1 ...Ai | Ai+1 .... Am-1
B0 B1 ...Bj | Bj+1 .... Bn-1
切分点满足条件是:
<1> 左边数量 == 右边数量(m+n为偶数) 或(左边数量 == 右边数量+1 (m+n)为奇数)
i+1+j+1=m-1-i-1+1+n-1-j-1+1==>2(i+j)=m+n-4 (m+n)为偶数
i+j+2=m-1-i-1+1+n-1-j-1+1 + 1==>2(i+j)=m+n-3 (m+n)为奇数
<2>max{left}<=min{right}
Ai<=Bj+1 (1)
Bj<=Ai+1 (2)
当条件(1)不满足,即Ai>Bj+1,可以通过较小i来减小Ai,并增大Bj+1
当条件(2)不满足,即Bj>Ai+1,可以通过增大i来增大Ai+1,并减少Bj
因此可通过二分搜索,来确定满足<2>条件的i,j.
<3> 边界条件 i∈[-1,m-1] ,j∈[-1,n-1]
集合A有可能全部属于left,或right。
因此i∈[-1,m-1] 当i=-1时,Aleft=∅ , 当i=m-1时,Aright=∅
同理j∈[-1,n-1]
故在搜索i时,利用<1>得到j后,除最终满足条件<2>外,还需有j∈[-1,n-1]。
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m=nums1.size();
int n=nums2.size();
auto &A=nums1;
auto &B=nums2;
//对i在[-1,m-1]中遍历 i=-1代表Aleft为空集,i=m-1代表Aright为空集
//此时要确保j在区间[-1,n-1]中 j=-1代表Bleft为空集,j=n-1代表Bright为空集
//采用左闭右闭方式i∈[-1,m-1]
int left=-1,right=m-1;
while(left<=right){
int i=(left+right)/2;
int j=