NC107 寻找峰值
给出一个长度为 n n n 的数组 a r r arr arr,找出下标 i i i,满足 a r r i − 1 ≤ a r r i ≤ a r r i + 1 arr_{i-1} \le arr_i \le arr_{i+1} arri−1≤arri≤arri+1。特别的:
- 当 i = 0 i = 0 i=0 时,可忽略 a r r i − 1 ≤ a r r i arr_{i-1} \le arr_i arri−1≤arri 的限制。
- 当 i = n − 1 i = n-1 i=n−1 时,可忽略 a r r i ≤ a r r i + 1 arr_i \le arr_{i+1} arri≤arri+1 的限制。
如果存在多个满足条件的下标,则输出最大的下标。为了方便描述,我们将这个下标成为最大峰值下标。
比如:
- 输入为 [2,2,1,2,8,8,4],最大峰值下标为 5
- 输入为 [1],最大峰值下标为 0
- 输入为 [1,2],最大峰值下标为 1
- 输入为 [2,1],最大峰值下标为 0
这个算是简单题了,按照题目寻找下标即可:枚举 i ∈ [ 0 , n ) i ∈[0,n) i∈[0,n),对每个 i i i 检查其左右两侧的元素是否存在且符合要求。
class Solution {
public:
int solve(int* arr, int n) {
int anw = 0;
for (int i = 0; i < n; i++) {
bool left = true, right = true;
if (i != 0 && arr[i-1] > arr[i]) {
left = false;
}
if (i != n-1 && arr[i] < arr[i+1]) {
right = false;
}
if (left && right) {
anw = i;
}
}
return anw;
}
};
考虑到要寻找「最大的」满足限制的下标,因此我们倒着枚举位置 i i i,在找到第一个满足限制的 i i i 时可直接返回,可省略变量 a n w anw anw。
class Solution {
public:
int solve(int* arr, int n) {
for (int i = n-1; i >= 0; i--) {
bool left = true, right = true;
if (i != 0 && arr[i-1] > arr[i]) {
left = false;
}
if (i != n-1 && arr[i] < arr[i+1]) {
right = false;
}
if (left && right) {
return i;
}
}
return 0;
}
};
当你读完上面这段代码时,是否意识到一个问题:最大峰值下标一定存在吗?接下来,先证明一下最大峰值下标的存在性。
考虑长度为 1 1 1 的情形,最大峰值下标必然为 0 0 0。利用数学归纳法,先假设长度为 n n n 的数组 a r r arr arr 必然存在最大峰值下标 i i i。我们向 a r r arr arr 尾部添加任意元素 x x x,构造一个长度为 n + 1 n+1 n+1 的数组 a r r ′ arr' arr′。 x x x 与 a r r n − 1 arr_{n-1} arrn−1 的大小关系可分为以下两种:
- a r r n − 1 > x arr_{n-1} \gt x arrn−1>x,则 a r r ′ arr' arr′ 的最大峰值下标仍为 i i i。
- a r r n − 1 ≤ x arr_{n-1} \le x arrn−1≤x,则 a r r ′ arr' arr′ 的最大峰值下标为 n n n。
综上所述,当数组不为空时,最大峰值下标必然存在。利用上述证明过程,甚至可以 O ( n ) O(n) O(n) 的求出任意前缀的最大峰值下标。
class Solution {
public:
int solve(int* arr, int n) {
std::vector<int> anw(n, 0);
for (int i = 1; i < n; i++) {
if (arr[i-1] <= arr[i]) {
anw[i] = i;
} else {
anw[i] = anw[i-1];
}
}
return anw[n-1];
}
};
分析上述代码,不难发现 a n w n − 1 anw_{n-1} anwn−1 必然满足:
- 当 n = 1 n = 1 n=1 时, a n w n − 1 = a n w 0 = 0 anw_{n-1} = anw_{0} = 0 anwn−1=anw0=0。
- 当 n > 1 n > 1 n>1 时, a n w n − 1 anw_{n-1} anwn−1 为最大的满足 a r r i − 1 ≤ a r r i arr_{i-1} \le arr_{i} arri−1≤arri 的下标 i i i。
于是,代码可以精简为:
class Solution {
public:
int solve(int* arr, int n) {
for (int i = n-1; i >= 1; i--) {
if (arr[i] > arr[i-1]) {
return i;
}
}
return 0;
}
};