845. 数组中的最长山脉
题目描述
把符合下列属性的数组 arr
称为 山脉数组 :
arr.length >= 3
- 存在下标
i
(0 < i < arr.length - 1
),满足arr[0] < arr[1] < ... < arr[i - 1] < arr[i]
arr[i] > arr[i + 1] > ... > arr[arr.length - 1]
给出一个整数数组 arr
,返回最长山脉子数组的长度。如果不存在山脉子数组,返回 0
。
示例 1:
输入:arr = [2,1,4,7,3,2,5] 输出:5 解释:最长的山脉子数组是 [1,4,7,3,2],长度为 5。
示例 2:
输入:arr = [2,2,2] 输出:0 解释:不存在山脉子数组。
提示:
1 <= arr.length <= 104
0 <= arr[i] <= 104
进阶:
- 你可以仅用一趟扫描解决此问题吗?
- 你可以用
O(1)
空间解决此问题吗?
解法
方法一:预处理 + 枚举
我们定义两个数组 $f$ 和 $g$,其中 $f[i]$ 表示以 $arr[i]$ 结尾的最长上升子序列的长度,而 $g[i]$ 表示以 $arr[i]$ 开头的最长下降子序列的长度。那么对于每个下标 $i$,如果 $f[i] \gt 1$ 且 $g[i] \gt 1$,那么以 $arr[i]$ 为山顶的山脉的长度为 $f[i] + g[i] - 1$,我们只需要枚举所有的 $i$,找出最大的那个值即可。
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $arr$ 的长度。
Python3
class Solution:
def longestMountain(self, arr: List[int]) -> int:
n = len(arr)
f = [1] * n
g = [1] * n
for i in range(1, n):
if arr[i] > arr[i - 1]:
f[i] = f[i - 1] + 1
ans = 0
for i in range(n - 2, -1, -1):
if arr[i] > arr[i + 1]:
g[i] = g[i + 1] + 1
if f[i] > 1:
ans = max(ans, f[i] + g[i] - 1)
return ans
Java
class Solution {
public int longestMountain(int[] arr) {
int n = arr.length;
int[] f = new int[n];
int[] g = new int[n];
Arrays.fill(f, 1);
Arrays.fill(g, 1);
for (int i = 1; i < n; ++i) {
if (arr[i] > arr[i - 1]) {
f[i] = f[i - 1] + 1;
}
}
int ans = 0;
for (int i = n - 2; i >= 0; --i) {
if (arr[i] > arr[i + 1]) {
g[i] = g[i + 1] + 1;
if (f[i] > 1) {
ans = Math.max(ans, f[i] + g[i] - 1);
}
}
}
return ans;
}
}
C++
class Solution {
public:
int longestMountain(vector<int>& arr) {
int n = arr.size();
int f[n];
int g[n];
fill(f, f + n, 1);
fill(g, g + n, 1);
for (int i = 1; i < n; ++i) {
if (arr[i] > arr[i - 1]) {
f[i] = f[i - 1] + 1;
}
}
int ans = 0;
for (int i = n - 2; ~i; --i) {
if (arr[i] > arr[i + 1]) {
g[i] = g[i + 1] + 1;
if (f[i] > 1) {
ans = max(ans, f[i] + g[i] - 1);
}
}
}
return ans;
}
};
Go
func longestMountain(arr []int) (ans int) {
n := len(arr)
f := make([]int, n)
g := make([]int, n)
for i := range f {
f[i] = 1
g[i] = 1
}
for i := 1; i < n; i++ {
if arr[i] > arr[i-1] {
f[i] = f[i-1] + 1
}
}
for i := n - 2; i >= 0; i-- {
if arr[i] > arr[i+1] {
g[i] = g[i+1] + 1
if f[i] > 1 {
ans = max(ans, f[i]+g[i]-1)
}
}
}
return
}
方法二:一次遍历(枚举左侧山脚)
我们可以枚举山脉的左侧山脚,然后向右寻找山脉的右侧山脚。我们可以使用两个指针 $l$ 和 $r$,其中 $l$ 表示左侧山脚的下标,$r$ 表示右侧山脚的下标,初始时 $l=0$,$r=0$,然后我们向右移动 $r$,找到山顶的位置,此时判断 $r$ 是否满足 $r + 1 \lt n$ 并且 $arr[r] \gt arr[r + 1]$,如果满足,我们向右继续移动 $r$,直到找到右侧山脚的位置,此时山脉的长度为 $r - l + 1$,我们更新答案,然后将 $l$ 的值更新为 $r$,继续寻找下一个山脉。
时间复杂度 $O(n)$,空间复杂度 $O(1)$。其中 $n$ 为数组 $arr$ 的长度。
Python3
class Solution:
def longestMountain(self, arr: List[int]) -> int:
n = len(arr)
ans = l = 0
while l + 2 < n:
r = l + 1
if arr[l] < arr[r]:
while r + 1 < n and arr[r] < arr[r + 1]:
r += 1
if r < n - 1 and arr[r] > arr[r + 1]:
while r < n - 1 and arr[r] > arr[r + 1]:
r += 1
ans = max(ans, r - l + 1)
else:
r += 1
l = r
return ans
Java
class Solution {
public int longestMountain(int[] arr) {
int n = arr.length;
int ans = 0;
for (int l = 0, r = 0; l + 2 < n; l = r) {
r = l + 1;
if (arr[l] < arr[r]) {
while (r + 1 < n && arr[r] < arr[r + 1]) {
++r;
}
if (r + 1 < n && arr[r] > arr[r + 1]) {
while (r + 1 < n && arr[r] > arr[r + 1]) {
++r;
}
ans = Math.max(ans, r - l + 1);
} else {
++r;
}
}
}
return ans;
}
}
C++
class Solution {
public:
int longestMountain(vector<int>& arr) {
int n = arr.size();
int ans = 0;
for (int l = 0, r = 0; l + 2 < n; l = r) {
r = l + 1;
if (arr[l] < arr[r]) {
while (r + 1 < n && arr[r] < arr[r + 1]) {
++r;
}
if (r + 1 < n && arr[r] > arr[r + 1]) {
while (r + 1 < n && arr[r] > arr[r + 1]) {
++r;
}
ans = max(ans, r - l + 1);
} else {
++r;
}
}
}
return ans;
}
};
Go
func longestMountain(arr []int) (ans int) {
n := len(arr)
for l, r := 0, 0; l+2 < n; l = r {
r = l + 1
if arr[l] < arr[r] {
for r+1 < n && arr[r] < arr[r+1] {
r++
}
if r+1 < n && arr[r] > arr[r+1] {
for r+1 < n && arr[r] > arr[r+1] {
r++
}
ans = max(ans, r-l+1)
} else {
r++
}
}
}
return
}