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,所以,說白了,就是不允許我們使用普通的順序查找,限制了時間複雜度。
將這些條件綜合一下,就只有一個思路了,二分搜索,爲什麼想到二分搜索呢?
- 第一,數組局部有序,山頂左邊升序,右邊降序。
- 第二,搜索次數有限,只有二分搜索能夠達到這種目標。
那麼我們應該如何對局部去搜索呢?
- 第一,局部有序肯定不能去全局的搜索一個值,只能去尋找局部有序的分界線。
- 第二,這種局部有序中的搜索一定是分別在有序的部分裏搜索的。
綜合一下這兩個判斷條件,我們可以發現,我們需要這個分界線的位置,利用這個位置對兩邊分別進行二分搜索,同時,這個分界線又可以根據全局的二分搜索去找到。
也就是說,我們需要進行三次二分搜索:
- 全局搜索,得到區間分界線的下標。
- 在左邊有序部分進行搜索。
- 如果左邊沒有搜索到,就繼續在右邊進行搜索。
我們來看一下具體是如何去進行二分搜索的。
二分搜索的三大要點:
- 循環結束條件。
- 中間點選取方式。
- 邊界收縮行爲。
如果三種方式選取的不一樣,最後得到的結果的值會在不同的地方取得。
對於第一次搜索,我們的目標是查找分界點,實質上,還是查找第一個滿足條件(單調減)的下標值,這種查找第一個滿足條件的問題,最常用的循環條件就是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