LeetCode二分查找总结

对3道二分查找题的总结,来源于:soulmachine,code_ganker

Search Insert Position

Search in Rotated Sorted Array

Search in Rotated Sorted Array II


1.最基本的二分查找:

思路就是每次取中间,如果等于目标即返回;否则根据大小关系切去一半。

因此算法复杂度是O(logn),空间复杂度O(1)。  

<span style="font-size:12px;">public class Solution {  
    public int searchInsert(int[] A, int target) {  
        int left=0;  
        int right=A.length-1;  
        while(left<=right){//【注意1】
            int mid=(left+right)/2;  
            if(target==A[mid]) //find it!
                return mid;  
            if (target<A[mid])//left half  
                right=mid-1;  
            else              //right half  
                left=mid+1;  
        }  
        return left;//should insert here【注意3】
    }  
} </span>

【注意1】while条件为left<=right,若找不到目标,最后一次操作是left和right指向同一index,执行一次循环体;结束时left>right,左右指针位置颠倒。  
【注意2】先写leftHalf,再写rightHalf
【注意3】"以上实现有一个好处:循环结束时,如果没找到目标,那么left一定停在恰好比目标大的index上,right一定停在恰好比目标小的index上,比较推荐这种实现。"  


2.旋转数组的查找I:

基本二分查找比较mid的值,这里比较target是否在区间,别无特殊。
不用纠结于Array怎么无序的,关注于旋转后一定有half是有序的。

<span style="font-size:12px;">public class Solution {
    public int search(int[] A, int target) {
        int left=0;
        int right=A.length-1;
        while(left<=right){    //循环条件和基本二分查找一样
            int mid=(left+right)/2;
            if(A[mid]==target)
                return mid;
            if(A[left]<=A[mid]){   //leftHalf有序【注意1】
                if(A[left]<=target && target<A[mid])//【注意2】
                    right=mid-1;
                else
                    left=mid+1;
            }
            else{                //既A[left]>A[mid],leftHalf无序【注意3】
                if(A[mid]<target && target<=A[right])
                    left=mid+1;
                else
                    right=mid-1;
            }
        }
        return -1;
    }
}</span>

【注意1】条件必须是 A[left] <= A[mid],因为考虑到 left == mid 的case。例如:只写"<",那么考虑 A=[3,1], target=1 的测试用例,结果会是错误的"-1"。

【注意2】注意写区间时候按照[,)来写。判断target是否在有序的half里,思路较清楚。

  至于为什么区间含left(或right)不含mid,因为target可能在两个边缘,如果在mid,就会直接return。

【注意3】条件写成 if ( A[left] > A[mid] ) -- else 也可以。


【注意4】关注于leftHalf是否有序,条件只能写成" if ( A[left] <= A[mid] ) -- else ",原因如上。(Soulmachine Ver.)
  关注于rightHalf是否有序,条件可以写成" if ( A[mid] < A[right] ) -- else ",或" if ( A[mid] < A[right] ) -- else ",OJ均会accept。(Code_Ganker Ver.)


3.旋转数组的查找II:

和上题一样,只是因为元素重复,if(A[left]<=A[mid])不一定是有序的,需要将这个case拆解成两部分处理:

-若 A[left] < A[mid] ,按照有序处理;

-若 A[left] ==A[mid],left++,处理边缘再来查找就ok。 

<span style="font-size:12px;">public class Solution {
    public boolean search(int[] A, int target) {
        int left=0;
        int right=A.length-1;
        while(left<=right){
            int mid=(left+right)/2;
            if(A[mid]==target)
                return true;
            if(A[left]<A[mid]){
                if(A[left]<=target && target<A[mid])
                    right=mid-1;
                else
                    left=mid+1;
            }
            else if(A[left]==A[mid]){
                left++;
            }
            else{
                if(A[mid]<target && target<=A[right])
                    left=mid+1;
                else
                    right=mid-1;
            }
        }
        return false;
    }
}</span>
【注意1】"对left移动一步,直到边缘和中间不在相等或者相遇,这就导致了会有不能切去一半的可能。所以最坏情况(比如全部都是一个元素,或者只有一个元素不同于其他元素,而他就在最后一个)就会出现每次移动一步,总共是n步,算法的时间复杂度变成O(n)。"


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值