山脈數組中查找目標值--二分搜索

0x01.問題

給你一個 山脈數組 mountainArr,請你返回能夠使得 mountainArr.get(index) 等於 target 最小 的下標 index 值。

如果不存在這樣的下標 index,就請返回 -1

何爲山脈數組?如果數組 A 是一個山脈數組的話,那它滿足如下條件:

首先A.length >= 3

其次,在 0 < i < A.length - 1 條件下,存在 i 使得:

  • A[0] < A[1] < ... A[i-1] < A[i]
  • A[i] > A[i+1] > ... > A[A.length - 1]

你將 不能直接訪問該山脈數組,必須通過 MountainArray 接口來獲取數據:

  • MountainArray.get(k) - 會返回數組中索引爲k 的元素(下標從 0 開始)
  • MountainArray.length() - 會返回該數組的長度

注意:

MountainArray.get 發起超過 100 次調用的提交將被視爲錯誤答案。

示例 1:

輸入:array = [1,2,3,4,5,3,1], target = 3 輸出:2 解釋:3 在數組中出現了兩次,下標分別爲 2 和
5,我們返回最小的下標 2。

示例 2:

輸入:array = [0,1,2,4,2,1], target = 3 輸出:-1 解釋:3 在數組中沒有出現,返回 -1。

提示:

  • 3 <= mountain_arr.length() <= 10000
  • 0 <= target <= 10^9
  • 0 <= mountain_arr.get(index) <= 10^9
 * // This is MountainArray's API interface.
 * // You should not implement it, or speculate about its implementation
 * interface MountainArray {
 *     public int get(int index) {}
 *     public int length() {}
 * }
public int findInMountainArray(int target, MountainArray mountainArr)

0x02.簡要分析思路

看一下題目,大概是這個樣子的:

  • 山脈數組其實就是數組中有一個值,滿足在左邊單調增,右邊單調減。
  • 不能夠直接訪問數組,必須通過接口。
  • 不能訪問超過100次,但是數組長度有10000,所以,說白了,就是不允許我們使用普通的順序查找,限制了時間複雜度。

將這些條件綜合一下,就只有一個思路了,二分搜索,爲什麼想到二分搜索呢?

  • 第一,數組局部有序,山頂左邊升序,右邊降序。
  • 第二,搜索次數有限,只有二分搜索能夠達到這種目標。

那麼我們應該如何對局部去搜索呢?

  • 第一,局部有序肯定不能去全局的搜索一個值,只能去尋找局部有序的分界線。
  • 第二,這種局部有序中的搜索一定是分別在有序的部分裏搜索的。

綜合一下這兩個判斷條件,我們可以發現,我們需要這個分界線的位置,利用這個位置對兩邊分別進行二分搜索,同時,這個分界線又可以根據全局的二分搜索去找到。

也就是說,我們需要進行三次二分搜索:

  1. 全局搜索,得到區間分界線的下標。
  2. 在左邊有序部分進行搜索。
  3. 如果左邊沒有搜索到,就繼續在右邊進行搜索。

我們來看一下具體是如何去進行二分搜索的。
二分搜索的三大要點:

  • 循環結束條件。
  • 中間點選取方式。
  • 邊界收縮行爲。

如果三種方式選取的不一樣,最後得到的結果的值會在不同的地方取得。

對於第一次搜索,我們的目標是查找分界點,實質上,還是查找第一個滿足條件(單調減)的下標值,這種查找第一個滿足條件的問題,最常用的循環條件就是while(left<right)

  • 對於這種循環條件,退出循環的條件是left=right,說明最後的答案左右都可以。
  • 然後確定一下邊界收縮的方式,對於搜索邊界值而言,我們無妨將邊界放到左邊,即分爲[left, mid][mid + 1, right],此時中點選取方式爲mid=left+(right-left)/2,若分到右邊,則選取爲mid=left+(right-left+1)/2

對於第二和第三次搜索,是具體的搜索指定的值了,一般就採用while(left<=right),可以把區間分爲三個部分:[left,mid-1],[mid],[mid+1,right]

確定了這些,就可以很快速的寫出二分的代碼了,具體的細節看代碼。

0x03.解決代碼–二分搜索

class Solution {
    public int findInMountainArray(int target, MountainArray mountainArr) {
        int left=0,right=mountainArr.length()-1;
        //第一次二分搜索,搜索山頂
        while(left<right){
            int mid=left+(right-left)/2;
            if(mountainArr.get(mid)<mountainArr.get(mid+1)){
                left=mid+1;
            }else{
                right=mid;
            }
        }
        int top=left;
        left=0;
        right=top;
        int index=-1;
        //第二次二分搜索,搜索山頂左邊的區間
        while(left<=right){
            int mid=left+(right-left)/2;
            int midValue=mountainArr.get(mid);
            if(midValue==target){
                return mid;
            }else if(midValue<target){
                left=mid+1;
            }else{
                right=mid-1;
            }
        }     
        left=top+1;
        right=mountainArr.length()-1;
        //第三次二分搜索,搜索山頂右邊的區間
        while(left<=right){
            int mid=left+(right-left)/2;
            int midValue=mountainArr.get(mid);
            if(midValue==target){
                return mid;
            }else if(midValue>target){
                left=mid+1;
            }else{
                right=mid-1;
            }
        }
        return -1;
    }
}

ATFWUS --Writing By 2020–04-29

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章