二分查找算法的靈活運用

二分查找算法一般要求數(shù)組是有序的,應用過程中必須要設置一個基準值,用數(shù)組中間部位的值與基準值比較進而更新查找區(qū)間。

二分查找算法的基本框架:

public int searchNum(int[]nums,int target){
    int l=0;
    int r=nums.length-1;
    while(l<=r){
        int mid=(l+r)/2;
        if(nums[mid]==target){
            return mid;
        }else if(target<nums[mid]){
            r=mid-1;
        }else{
            l=mid+1;
        }
    }
    return -1;//表示沒有找到該數(shù)字
}

二分查找算法的邊界問題:

情況1:在區(qū)間[left,right)內使用二分查找算法尋找答案,注意這里的區(qū)間是左閉右開的。在更新right時,只能是right=mid,其中mid=left+(right-left)/2。

那么為什么不能是right=mid-1呢?理由很簡單,假設將right更新為mid-1,則查找區(qū)間變成了[left,mid-1),因為右邊是開區(qū)間,所以mid-1是查找不到的,而更新right之前只能說明索引mid處不是我們要查找的答案,并不能說明mid-1處也不是正確答案,mid-1處仍然是要考慮的??傊?,開區(qū)間的一側一定不能縮小區(qū)間。而left要更新為mid+1,才能保證區(qū)間里的每個元素都能被訪問到。

情況2:在[left,right]內使用二分查找尋找答案,這里是左閉右閉的區(qū)間。根據(jù)情況1的分析可知更新區(qū)間應為:left=mid+1,right=mid-1,可以保證區(qū)間里的每個元素都能被訪問到。

經(jīng)過上述分析,可以說運用二分查找算法的總體流程應分為3個步驟:

1、確定基準值,將這一步驟單獨拿出來主要是為了針對二分查找算法的一些變體。

2、確定查找區(qū)間,及區(qū)間的更新方式。

3、調試結果。

1、旋轉數(shù)組中的最小數(shù)字

JZ6題目描述:

把一個數(shù)組最開始的若干個元素搬到數(shù)組的末尾,我們稱之為數(shù)組的旋轉。輸入一個非遞減排序的數(shù)組的一個旋轉,輸出旋轉數(shù)組的最小元素。
NOTE:給出的所有元素都大于0,若數(shù)組大小為0,請返回0。

示例:

輸入
[3,4,5,1,2]
返回值
1

解答:

本題考查二分查找算法的靈活運用,有序數(shù)組旋轉后會變成兩部分有序的數(shù)組。

本題的基準值設置為查找區(qū)間最右邊的值,那能不能將左左邊的值設置為基準值吶,答案是不能的,理由如下,假如有一個數(shù)組為[1,2,3,4,5],基準值target=1,中間值mid=3,此時mid是大于基準值的,這種情況下答案1在mid的左邊?,F(xiàn)有另一數(shù)組[3,4,5,1,2],基準值target=3,中間值mid=5,mid仍然是大于基準值的,而這種情況下答案1卻在mid的右邊。意思是將基準值設置在最左邊,對于同樣中間值大于基準值的情況,會出現(xiàn)兩種情況,有時候答案在左部分,有時候卻在右部分,因此無法正確更新查找區(qū)間。

public int minNumberInRotateArray(int [] array){
        //一定要選擇最右邊的元素作為基準值
        int left=0;
        int right=array.length-1;
        if(right<0)return 0;
        //int target=array[right];
        while(left<right){
            int mid=left+(right-left)/2;
            if(array[mid]>array[right]){
                left=mid+1;
            }
            else if(array[mid]<array[right]){
                right=mid;
            }
            else{
                //如果是 1 0 1 1 1, arr[mid] =    target = 1, 顯然答案在左邊
                //如果是 1 1 1 0 1, arr[mid] = target = 1, 顯然答案在右邊
                //所以這種情況,不能確定答案在左邊還是右邊,那么就讓last = last - 1;慢慢縮少區(qū)間,同時也不會錯過答案。
                 right--;
            }
        }
        return array[right];
    }
2、二維數(shù)組中的查找

JZ1題目描述:

在一個二維數(shù)組中(每個一維數(shù)組的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數(shù),輸入這樣的一個二維數(shù)組和一個整數(shù),判斷數(shù)組中是否含有該整數(shù)。

輸入
7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回值
true

解答

該二維數(shù)組每一行和每一列中的元素都是遞增排列的,因此可以使用二分查找算法的思想來查找元素。運用二分查找算法的時候,選定的中間值mid必須滿足整體有序的,即在上述示例中可以選擇array[0] [3]作為中間值mid, 該mid值所在行是遞增的,所在列也是遞增的。

public class Solution {
    //對二維有序數(shù)組進行二分查找
    public boolean Find(int target, int [][] array) {
        int row=array.length;
        if(row==0) return false;
        int col=array[0].length;
        int i=0;int j=col-1;
        while(i<row&&j>=0){
            if(target>array[i][j]){
                i++;
            }
            else if(target<array[i][j]){
                j--;
            }
            else{
                return true;
            }
        }
        return false;
    }
}
3、查找左右邊界

假設有一個有序數(shù)組{1,2,2,2,3},這個時候讓你查找target=2的邊界,便可以使用二分查找算法來分別查找有序數(shù)組的左右邊界。

查找左邊界:

public int findLeftBound(int[] nums,int target){
    int left=0;
    int right=nums.length-1;
    while(left<right){//注意:區(qū)間對應
        int mid=left+(right-left)/2;
        if(target<=nums[mid]){
            right=mid;
        }
        else{
            left=mid+1;
        }
    }
    return left;
}

查找右邊界:

public int findRightBound(int[] nums,int target){
    int left=0;
    int right=nums.length-1;
    while(left<right){注意:區(qū)間對應
        int mid=left+(right-left)/2;
        if(target<nums[mid]){
            right=mid-1;
        }
        else{
            left=mid;
        }
    }
    return right;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容