球球速刷LC之二分查找 二轮

二分查找基本模板

关键 :
初始化左闭右开
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=
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值