题目描述
(这是一个 交互式问题)
所谓 山脉数组,如果数组 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]
给你一个山脉数组 mountainArr
,请你返回能够使得 mountainArr.get(index)
等于 target
最小 的下标 index
值。如果不存在这样的下标 index
,就请返回 -1
。
你将 不能 直接访问该山脉数组,必须通过 MountainArray
接口来获取数据:
MountainArray.get(k)
会返回数组中索引为k
的元素(下标从 0 开始)。MountainArray.length()
会返回该数组的长度。
对 MountainArray.get
发起超过 100
次调用的提交将被视为_错误答案_。此外,任何试图规避判题系统的解决方案都将会导致比赛资格被取消。
样例
输入:array = [1,2,3,4,5,3,1], target = 3
输出:2
解释:3 在数组中出现了两次,下标分别为 2 和 5,我们返回最小的下标 2。
输入:array = [0,1,2,4,2,1], target = 3
输出:-1
解释:3 在数组中没有出现,返回 -1。
提示
3 <= mountainArr.length() <= 10000
0 <= target <= 10^9
0 <= mountainArr.get(index) <= 10^9
算法
(三分查找 + 二分查找) $O(\log n)$
- 通过三分查找定位山的最高点,然后通过两次二分查找分别在山峰的左边和右边查找目标值。
- 三分查找过程如下:假设待查询的闭区间为
[l, r]
。- 如果闭区间长度严格大于 3,则将区间平均切割为三个部分。
- 令
mid1 = l + (r - l) / 3
和mid2 = r - (r - l) / 3
,三个区间分别为[l, mid1]
、[mid1, mid2]
和[mid2, r]
。 - 如果
mountainArr.get(mid1) > mountainArr.get(mid2)
,则r = mid2
,因为mid2
右侧的区间就没有查询的必要了。 - 否则
l = mid1
,道理同样是mid1
左侧的区间就没有查询的必要了。 - 最终
l
和r
的差距少于等于 3 时,退出循环,暴力判断[l, r]
内哪个点最大。
- 接下来两次二分查找的过程不再赘述。
时间复杂度
- 三分查找和二分查找的时间复杂度都是 $O(\log n)$。
空间复杂度
- 仅需要定义常数个变量,故空间复杂度是 $O(1)$。
C++ 代码
/**
* // This is the MountainArray's API interface.
* // You should not implement it, or speculate about its implementation
* class MountainArray {
* public:
* int get(int index);
* int length();
* };
*/
class Solution {
public:
int getHighest(int n, MountainArray &mountainArr) {
int l = 0, r = n - 1;
while (l + 2 < r) {
int mid1 = l + (r - l) / 3;
int mid2 = r - (r - l) / 3;
if (mountainArr.get(mid1) > mountainArr.get(mid2)) {
r = mid2;
} else {
l = mid1;
}
}
if (l == r) {
return l;
} else if (l + 1 == r) {
if (mountainArr.get(l) > mountainArr.get(r))
return l;
else
return r;
} else {
int x = mountainArr.get(l), y = mountainArr.get(l + 1), z = mountainArr.get(l + 2);
if (x > y && x > z)
return l;
else if (y > x && y > z)
return l + 1;
else
return l + 2;
}
}
int findInMountainArray(int target, MountainArray &mountainArr) {
int n = mountainArr.length();
int highest = getHighest(n, mountainArr);
int l = 0, r = highest;
while (l < r) {
int mid = (l + r) >> 1;
if (mountainArr.get(mid) < target) {
l = mid + 1;
} else {
r = mid;
}
}
if (mountainArr.get(l) == target)
return l;
l = highest; r = n - 1;
while (l < r) {
int mid = (l + r) >> 1;
if (mountainArr.get(mid) > target) {
l = mid + 1;
} else {
r = mid;
}
}
if (mountainArr.get(l) == target)
return l;
return -1;
}
};
递归实现。把retrieve arr element 的方式 functor 记忆化了 来满足要求。 如果这个functor 是mutable 我并不知道如何pass 这个 如果 by const auto &则无法修改。 如果auto 则可能会产生拷贝。还往指教。