LeetCode刷题之二分

二分

二分是十分基础的一个算法,但是二分对于有序元素的查找应用效果十分优秀。由于最近在刷LeetCode,有二分的题目,感觉有不少感受,觉得以前对于二分也没有太重视,毕竟有对应的库函数可以使用,借此机会,写一篇博客,记录题目和感受。

二分的关键点

  • 元素有序,是能按照一个给定原则排序的
  • 明确需要二分达到的目的(例如我要寻找一个确切的一个元素还是一个区间)
  • 确定寻找时区间端点的变化,已经结束的条件

搜索某个确切元素
704. 二分查找
在这里插入图片描述
这题我想达到最后搜索区间只剩这个要找到的数(如果找得到的话),那么最终结果会是
s t a r t   =   e n d \qquad\qquad\qquad\qquad\qquad\qquad\qquad start\ =\ end start = end
那么 s t a r t   ≤   e n d start\ \leq \ end start  end,作为循环套件
{ e n d   =   M i d   −   1 n u m s   [   M i d   ]   >   t a r g e t s t a r t   =   M i d   +   1 n u m s   [   M i d   ]   <   t a r g e t 找 到 数 的 下 标 M i d n u m s   [   M i d   ]   =   t a r g e t \begin{cases} end\ =\ Mid\ -\ 1 & nums\ [\ Mid\ ]\ >\ target \\ start\ =\ Mid\ +\ 1 & nums\ [\ Mid\ ]\ <\ target \\ 找到数的下标Mid & nums\ [\ Mid\ ]\ =\ target \end{cases} end = Mid  1start = Mid + 1Midnums [ Mid ] > targetnums [ Mid ] < targetnums [ Mid ] = target

int search(int* nums, int numsSize, int target){
    int start = 0,end = numsSize-1,Mid = (start + end)/2,ans = -1;
    while(start<=end){//最后寻找到一个元素,区间最后有一个元素即可
        if(nums[Mid]>target)end = Mid-1;//右区间都不符合条件,将右区间整个剔除
        else if(nums[Mid]<target) start = Mid + 1;//左区间都不符合条件,将左区间整个剔除
        else {//找到
                ans = Mid;
                break;
        }
        Mid = (start + end)/2;
    }
    //找不到了
    return ans;
}

。。。
在已有序列中寻找元素,或(元素不存在时)元素可以插入的位置。
35. 搜索插入位置
在这里插入图片描述这题很好想,查找和上一题一样,是常规二分查找。但是最后如果没查找到,就有一些不同。
我们没有提前查找到的时候,有两种情况,区间都只包含1个值了, [   s t a r t   (   e n d   )   ] [\ start\ (\ end\ )\ ] [ start ( end ) ].
此时 M i d   =   s t a r t Mid\ =\ start Mid = start,若此时元素正是要找的,我们直接返回。若不是,要么小了, s t a r t + + start++ start++
要么,大了, e n d − − end-- end,无论是要插入的值处于边界,还是中间,都会落在 s t a r t start start上,返回 s t a r t start start即可

int searchInsert(int* nums, int numsSize, int target){
    int start = 0,end = numsSize - 1,Mid = start + (end-start)/2;
    while(start<=end){
        if(nums[Mid]<target){
            start = Mid+1;
        }
        else if(nums[Mid]>target){
            end = Mid-1;
        }
        else{
            break;
        }
        Mid = start + (end-start)/2;
    }
    if(start>end)Mid = start;
    return Mid;
}

寻找一系列相同元素中的第一个元素(类似lower_bound())
278. 第一个错误的版本

这题我们可以在搜索结束的时候,
e n d   =   s t a r t \qquad\qquad\qquad\qquad\qquad\qquad\qquad end \ =\ start end = start
其中 e n d end end对应第一个第一个错误版本,返回结束时的 e n d end end即可。
分析:
i s B a d V e r s i o n ( M i d ) = = t r u e isBadVersion(Mid)==true isBadVersion(Mid)==true, e n d = M i d end=Mid end=Mid可以确保 e n d end end的位置可以必是 B a d V e r s i o n BadVersion BadVersion
i s B a d V e r s i o n ( M i d ) = = f a l s e isBadVersion(Mid)==false isBadVersion(Mid)==false, s t a r t = M i d + 1 start=Mid+1 start=Mid+1可以确保 s t a r t start start的位置可以最多是第一个 B a d V e r s i o n BadVersion BadVersion
最终发现在退出是 e n d   =   s t a r t end \ =\ start end = start时,必是第一个 B a d V e r s i o n BadVersion BadVersion的位置。

// The API isBadVersion is defined for you.
// bool isBadVersion(int version);

int firstBadVersion(int n) {
    int start = 1,end = n;//Mid = (start + end)/2;(边界会溢出)
    int Mid = start + (end - start)/2;
    while(start<end){//控制最后结束时start最多和end重合
        if(isBadVersion(Mid)){
            end = Mid;//
        }
        else {
            start = Mid+1;//
        }
        Mid = start + (end - start)/2;
    }
    return end;

}

此题中如果使用 M i d = ( s t a r t + e n d ) / 2 Mid = (start +end)/2 Mid=start+end/2会爆int,可以使用 M i d = s t a r t + ( e n d − s t a r t ) / 2 Mid = start + (end - start)/2 Mid=start+(endstart)/2代替。


寻找要找寻所在区间(lower_bound,upper_bound)
34. 在排序数组中查找元素的第一个和最后一个位置
在这里插入图片描述首先利用二分找到一个满足条件的点的下标,然后以该点位分界线,将当前搜索区间分为两份,分别找该区间第一个目标值和最后一个目标值。同上一题的处理方法,处理第一个值,修改一下,找的最后一个目标值。

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* searchRange(int* nums, int numsSize, int target, int* returnSize){
    int start = 0,end = numsSize - 1,Mid = start + (end - start)/2;
   
    while(start<=end){
        if(nums[Mid]>target){
            end = Mid - 1;
        }
        else if(nums[Mid]<target){
            start = Mid + 1;
        }

        else{
            int st1 = start,en1 = Mid,M1 = st1 + (en1 - st1)/2;
            while(st1<en1){
                if(nums[M1] < target)st1 = M1 + 1;
                else en1 = M1;
                M1 = st1 + (en1- st1)/2;
            }
            start = st1;
            
           int st2 = Mid,en2 = end,M2 = st2 + (en2 - st2)/2;
            while(st2<en2){
                if(nums[M2]>target)en2 = M2 - 1;
                else st2 = M2;
                M2 = st2 + (en2 - st2 + 1)/2;
            }
            end = en2;
            break;
        }

        Mid = start + (end - start)/2;
    }

    *returnSize=2;
    int *ans=(int*)malloc(sizeof(int)*2);
    if(start>end){
       start = end = -1;
    }
    ans[0] =start,ans[1] = end;
    return ans;
}

练手二分习题:
33. 搜索旋转排序数组
74. 搜索二维矩阵

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值