zoukankan      html  css  js  c++  java
  • LeetCode总结--二分查找篇

    二分查找算法尽管简单,但面试中也比較常见。经经常使用来在有序的数列查找某个特定的位置。在LeetCode用到此算法的主要题目有:
    Search Insert Position
    Search for a Range
    Sqrt(x)
    Search a 2D Matrix
    Search in Rotated Sorted Array
    Search in Rotated Sorted Array II

    这类题目基本能够分为例如以下四种题型:
    1. Search Insert PositionSearch for a Range是考察二分查找的基本使用方法。基本思路是每次取中间,假设等于目标即返回,否则依据大小关系切去一半,因此时间复杂度是O(logn),空间复杂度O(1)。

    Search Insert Position为例,其关键代码写法例如以下:

        int l = 0;
        int r = A.length-1;
        while(l<=r)
        {
            int mid = (l+r)/2;
            if(A[mid]==target)
                return mid;
            if(A[mid]<target)
                l = mid+1;
            else
                r = mid-1;
        }
        return l;
    这样当循环停下来时,假设不是正好找到target,l指向的元素恰好大于target。r指向的元素恰好小于target,这里l和r可能越界。只是假设越界就说明大于(小于)target而且是最大(最小)。Search for a Range这道题能更好的解释这一点。

    其思路是先用二分查找找到当中一个target。然后再往左右找到target的边缘。我们主要看找边缘(往后找)的代码:

        int newL = m;
        int newR = A.length-1;
        while(newL<=newR)
        {
            int newM=(newL+newR)/2;
            if(A[newM]==target)
            {
                newL = newM+1;
            }
            else
            {
                newR = newM-1;
            }            
        }
        res[1]=newR;
    我们的目标是在后面找到target的右边界。由于左边界已经等于target,所以推断条件是相等则向右看,大于则向左看,依据上面说的,循环停下来时。l指向的元素应该恰好大于target。r指向的元素应该等于target。所以此时的r正是我们想要的。向前找边缘也同理。



    2. Sqrt(x)是数值处理的题目,但同一时候也能够用二分查找的思想来解决。由于我们知道结果的范围,取定左界和右界,然后每次砍掉不满足条件的一半,直到左界和右界相遇。算法的时间复杂度是O(logx),空间复杂度是O(1)。这里相同是考察二分查找的基本使用方法。代码例如以下:
    public int sqrt(int x) {
        if(x<0) return -1;
        if(x==0) return 0;
        int l=1;
        int r=x/2+1;
        while(l<=r)
        {
            int m = (l+r)/2;
            if(m<=x/m && x/(m+1)<m+1)
                return m;
            if(x/m<m)
            {
                r = m-1;
            }
            else
            {
                l = m+1;
            }
        }
        return 0;
    }
    这里要注意,这里推断相等的条件不是简单的 m == x/m, 而是 m<=x/m && x/(m+1)<m+1, 这是由于输出是整型。sqrt(14)=3 但 3 != 14/3. 所以我们须要一个范围框住结果。另外依据二分查找算法的特性,假设不能正好m==x/m停下,那么r指向的数字将正好是结果取整的值。

    所以我们也能够这样写:

    public int sqrt(int x) {
        if(x<0) return -1;
        if(x==0) return 0;
        int l=1;
        int r=x/2+1;
        while(l<=r)
        {
            int m = (l+r)/2;
            if(m==x/m )
                return m;
            if(x/m<m)
            {
                r = m-1;
            }
            else
            {
                l = m+1;
            }
        }
        return r;
    }
    3. Search a 2D Matrix是二分查找算法的多维应用,通过观察不难发现。输入的矩阵行内有序而且行间有序,所以查找仅仅须要先按行查找,定位出在哪一行之后再进行列查找就可以,两次二分查找,时间复杂度是O(logm+logn)。空间上仅仅需两个辅助变量。因而是O(1)。这里不再赘述。

    4. Search in Rotated Sorted ArraySearch in Rotated Sorted Array II算是二分查找算法的一个变体。

    Search in Rotated Sorted Array中,乍一看感觉数组已经不是有序的了。也就无法用二分查找算法,但细致分析一下会发现,由于仅仅rotate了一次,如果二分一下,总有一半是有序的,并且和还有一半无区间重叠,我们仅仅须要检查有序的一半的前后两个元素就能够确定target可能在哪一半。详细来说,如果数组是A,每次左边缘为l,右边缘为r。还有中间位置是m。

    在每次迭代中,分三种情况:
    (1)假设target==A[m],那么m就是我们要的结果,直接返回;
    (2)假设A[m]<A[r]。那么说明从m到r一定是有序的(没有受到rotate的影响),那么我们仅仅须要推断target是不是在m到r之间,假设是则把左边缘移到m+1,否则就target在还有一半,即把右边缘移到m-1。
    (3)假设A[m]>=A[r]。那么说明从l到m一定是有序的,相同仅仅须要推断target是否在这个范围内,对应的移动边缘就可以。
    依据以上方法,每次我们都能够切掉一半的数据。所以算法的时间复杂度是O(logn),空间复杂度是O(1)。

    Search in Rotated Sorted Array II中array有反复元素,依照刚刚的思路,二分之后尽管一半是有序的,但我们会遇到中间和边缘相等的情况,我们就丢失了哪边有序的信息。由于哪边都有可能是有序的结果。如果原数组是{1,2,3,3,3,3,3}。那么旋转之后有可能是{3,3,3,3,3,1,2},或者{3,1,2,3,3,3,3},这种我们推断左边缘和中心的时候都是3,如果我们要寻找1或者2,我们并不知道应该跳向哪一半。解决的办法仅仅能是对边缘移动一步。直到边缘和中间不在相等或者相遇,这就导致了会有不能切去一半的可能。

    所以最坏情况(比方所有都是一个元素。或者仅仅有一个元素不同于其它元素,而他就在最后一个)就会出现每次移动一步,总共是n步,算法的时间复杂度变成O(n)。

    整体来说。二分查找算法理解起来并不算难。但在实际面试的过程中可能会出现各种变体,怎样灵活的运用才是制胜的关键。

    我们要抓住“有序”的特点。一旦发现输入有“有序”的特点,我们就能够考虑能否够运用二分查找算法来解决该问题。

  • 相关阅读:
    买房的贷款时间是否是越长越好?https://www.zhihu.com/question/20842791
    asp.net cookie and session
    leelazero and google colab
    download file by python in google colab
    physical processor, core, logical processor
    通过powershell操作eventlog
    openxml in sql server
    get the page name from url
    How to Execute Page_Load() in Page's Base Class?
    Difference between HttpContext.Request and Request
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/7106749.html
Copyright © 2011-2022 走看看