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