LeetCode 每日一题笔记
0. 前言
- 日期:2026.05.07
- 题目:3660. 找到所有可以到达的最大值
- 难度:中等
- 标签:数组、贪心、前缀最值、递归
1. 题目理解
问题描述 :
给定一个整数数组 nums。对于数组中的每一个位置 i,你可以按照下面的规则跳跃任意次数:
- 向右跳:只能跳到比当前数小的数
- 向左跳:只能跳到比当前数大的数
请你返回一个数组 ans,其中 ans[i] 表示从位置 i 出发,能够到达的最大数值。
示例:
输入:nums = [2,1,3]
输出:[2,2,3]
2. 解题思路
核心观察
- 每个位置能到达的最大值,取决于左边的最大值是否能"打通"到更全局的最大值。
- 一旦左边出现比右侧最小值更大的数,就可以到达全局最大值。
- 可以用前缀最大值快速记录每个位置左侧的最大值及其下标。
- 递归思路:从右向左分割区间,用最大值把数组分成两段,分别赋值答案。
算法步骤
原版递归思路
- 预处理前缀最大值数组,记录每个位置的最大值及其下标。
- 从右往左递归处理:
- 找到当前区间的最大值位置
- 如果该最大值 > 右侧最小值,说明能到达全局最大值
- 给区间内所有位置赋值当前最大值
- 继续递归处理左半区间
优化版思路
- 先求一遍前缀最大值。
- 从右往左遍历,维护当前最小值
mn和当前可达最大值mx。 - 如果前缀最大值 > 当前最小值,说明能到达全局最大值,直接赋值。
- 否则更新最大值和最小值。
3. 代码实现
原版代码(递归 + 前缀最值)
java
package lc3660;
class Solution {
public void erFen(int[] nums, int right, int[] res, int[][] prevmax, int min, int max) {
if (right <= 0) {
return;
}
int nowMax = prevmax[right - 1][0];
int nowMaxLoc = prevmax[right - 1][1];
if (nowMax > min) {
nowMax = max;
}
for (int i = nowMaxLoc; i < right; i++) {
if (nums[i] < min) {
min = nums[i];
}
res[i] = nowMax;
}
right = nowMaxLoc;
erFen(nums, right, res, prevmax, min, max);
}
public int[] maxValue(int[] nums) {
int[] res = new int[nums.length];
int[][] prevMax = new int[nums.length][2];
int max = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > max) {
max = nums[i];
prevMax[i][0] = max;
prevMax[i][1] = i;
} else {
prevMax[i][0] = prevMax[i - 1][0];
prevMax[i][1] = prevMax[i - 1][1];
}
}
erFen(nums, nums.length, res, prevMax, Integer.MAX_VALUE, prevMax[nums.length - 1][0]);
return res;
}
}
优化版代码
java
class Solution {
public int[] maxValue(int[] nums) {
int n = nums.length;
int[] sum = new int[n];
sum[0] = nums[0];
for(int i = 1; i < n; i++){
sum[i] = Math.max(sum[i - 1], nums[i]);
}
int mn = nums[n - 1];
int mx = sum[n - 1];
for(int j = n - 1; j >= 0; j--){
if(sum[j] > mn){
sum[j] = mx;
}
else{
mx = sum[j];
}
mn = Math.min(mn, nums[j]);
}
return sum;
}
}
4. 代码优化说明
- 原版使用递归 + 前缀最大值数组,思路直观,通过分段赋值得到答案。
- 优化版使用一次前缀最大值 + 一次反向遍历,去掉递归,时间、空间更优。
- 优化版不再需要二维数组,仅用一个数组即可完成计算,代码更简洁。
5. 复杂度分析
原版代码
- 时间复杂度:O(n)O(n)O(n) ~ O(n2)O(n^2)O(n2) (取决于递归分割次数)
- 空间复杂度:O(n)O(n)O(n) (前缀数组 + 递归栈)
优化版代码
- 时间复杂度:O(n)O(n)O(n)
- 空间复杂度:O(n)O(n)O(n) (仅使用结果数组)
6. 总结
- 本题核心是判断每个位置能否到达全局最大值。
- 关键依据:如果左边有更大的数,且能"打通"到右侧,就能取全局最大。
- 从右往左遍历 + 维护最小/最大值,是本题最优思路。
- 递归版本易于理解,迭代优化版本效率更高。