echo

任生命穿梭 时间的角落

0%

山脉数组中查找目标值

1095. 山脉数组中查找目标值

(这是一个 交互式问题

给你一个 山脉数组 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 次调用的提交将被视为错误答案。此外,任何试图规避判题系统的解决方案都将会导致比赛资格被取消。

为了帮助大家更好地理解交互式问题,我们准备了一个样例 “答案”:https://leetcode-cn.com/playground/RKhe3ave,请注意这 不是一个正确答案

示例 1:

1
2
3
输入:array = [1,2,3,4,5,3,1], target = 3
输出:2
解释:3 在数组中出现了两次,下标分别为 2 和 5,我们返回最小的下标 2。

示例 2:

1
2
3
输入: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

山脉数组表示数组中间存在一个最大的数,它左边的数都是单调递增,右边的数都是单调递减。如果是已经排序的数组我们可以想到二分查找,那么山脉数组能不能也使用二分查找呢?

答案是可以,只要我们找到最高点的下标,将山脉数组分成左边一个单调递增的数组,右边分为一个单调递减的数组,然后分别对这两个数组进行二分查找。

问题的关键是如何找到最高点的下标,常规的做法是直接遍历一遍数组找到最大值的下标。更巧妙的方法是使用类似二分查找的方法来找到最高点的下标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while(left < right){
//计算中间点的下标
int mid = left + (right - left) / 2;

//mountainArr[mid] < mountainArr[mid+1]
//mountainArr[mid+1]比其左侧的值都大,其左侧的值不可能为最高点
if(mountainArr.get(mid) < mountainArr.get(mid + 1)){
//下一轮搜索区间[mid+1, right]
left = mid + 1;
}else{ //mountainArr[mid] >= mountainArr[mid+1]
//mountainArr[mid]比其左侧的值都大,其右侧的值不可能为最高点
//下一轮搜索区间[left, mid]
right = mid;
}
}

找到了最高点后,先在左边递增的部分寻找目标

1
2
3
4
5
6
7
8
9
10
11
12
while(left < right){
int mid = left + (right - left) / 2;
if(mountainArr.get(mid) < target){
//target 比 mountainArr[mid+1]左侧的值都大,其左侧的值不可能为target
//下一轮搜索区间[mid+1, right]
left = mid + 1;
}else{
//target 比 mountainArr[mid]右侧的值都小,其右侧的值不可能为target
//下一轮搜索区间[left, mid]
right = mid;
}
}

如果没找到就在右边递减的部分寻找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
while(left < right){
int mid = left + (right - left + 1) / 2;
if(mountainArr.get(mid) < target){
//target 比 mountainArr[mid-1]右侧的值都大,其右侧的值不可能为target
//下一轮搜索区间[left, mid - 1]
right = mid - 1;
}else{
//target 比 mountainArr[mid]左侧的值都小,其左侧的值不可能为target
//下一轮搜索区间[mid, right]
//[left(mid), right] --> [left, right(mid)] 将下取整改为上取整
//否则为死循环
left = mid;
}
}

最后将整合所有部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* // 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() {}
* }
*/

class Solution {
public int findInMountainArray(int target, MountainArray mountainArr) {
int len = mountainArr.length();
int peekIndex = findPeekIndex(mountainArr, 0, len-1);
if(mountainArr.get(peekIndex) == target){
return peekIndex;
}

int res = findSortedArray(target, mountainArr, 0, peekIndex - 1);
if(res != -1){
return res;
}

return findReverseArray(target, mountainArr, peekIndex + 1, len - 1);
}

private int findPeekIndex(MountainArray mountainArr, int left, int right){
while(left < right){
int mid = left + (right - left) / 2;
if(mountainArr.get(mid) < mountainArr.get(mid + 1)){
//下一轮搜索区间[mid+1, right]
left = mid + 1;
}else{
//下一轮搜索区间[left, mid]
right = mid;
}
}
// left == right
return left;
}

private int findSortedArray(int target, MountainArray mountainArr, int left, int right){
while(left < right){
int mid = left + (right - left) / 2;
if(mountainArr.get(mid) < target){
//下一轮搜索区间[mid+1, right]
left = mid + 1;
}else{
//下一轮搜索区间[left, mid]
right = mid;
}
}
if(mountainArr.get(left) == target){
return left;
}
return -1;
}

private int findReverseArray(int target, MountainArray mountainArr, int left, int right){
while(left < right){
int mid = left + (right - left + 1) / 2;
if(mountainArr.get(mid) < target){
//下一轮搜索区间[left, mid - 1]
right = mid - 1;
}else{
//下一轮搜索区间[mid, right]
//[left(mid), right] --> [left, right(mid)] 将下取整改为上取整
left = mid;
}
}
if(mountainArr.get(left) == target){
return left;
}
return -1;
}

}