区间动态规划例题:最长同值子数组的最大长度问题(相邻元素差不超过限制)
题目描述
给定一个整数数组 nums 和一个整数 limit,要求找到最长的连续子数组,使得该子数组中任意两个相邻元素的差的绝对值不超过 limit。你需要返回这个最长子数组的长度。
例如:
- 输入:
nums = [8, 2, 4, 7],limit = 4
输出:2
解释:满足条件的最长子数组是[2, 4]或[4, 7],长度均为 2。 - 输入:
nums = [10, 1, 2, 4, 7, 2],limit = 5
输出:4
解释:满足条件的最长子数组是[1, 2, 4, 7],相邻元素差最大为 3(|7-4|=3),且所有相邻差均 ≤5。
解题思路
此问题可以通过区间动态规划(Interval DP)解决。我们定义 dp[i][j] 表示子数组 nums[i..j] 是否满足相邻元素差不超过 limit。但直接使用二维 DP 会因 O(n²) 的空间和时间复杂度而效率较低。实际上,我们可以通过滑动窗口或单调队列优化到 O(n),但为了深入理解区间 DP 的思想,我们先从基础解法开始,再逐步优化。
步骤 1:基础区间 DP 定义
定义 dp[i][j] 为一个布尔值:
dp[i][j] = true表示子数组nums[i..j]中所有相邻元素差的绝对值 ≤limit。- 转移方程:
dp[i][j] = dp[i][j-1] && (|nums[j] - nums[j-1]| ≤ limit),前提是j > i。
即,若nums[i..j-1]满足条件,且新加入的nums[j]与nums[j-1]的差满足限制,则nums[i..j]也满足。
步骤 2:遍历与记录最大值
- 初始化:所有长度为 1 的子数组均满足条件(
dp[i][i] = true)。 - 按子数组长度
len从 2 到 n 遍历,对每个起始点i,计算终点j = i + len - 1,更新dp[i][j]并记录满足条件的最大长度。 - 时间复杂度:O(n²),空间复杂度:O(n²)。
步骤 3:优化思路
上述 DP 中,很多状态的计算是冗余的。实际上,问题等价于寻找最长的窗口 [i, j],使得窗口内最大值与最小值的差 ≤ limit(因为任意相邻差 ≤ limit 可推导出整个窗口的极差 ≤ limit,但反之不成立?注意:相邻差限制更强,但本题实际要求的是相邻差,而非整个窗口的极差。但若整个窗口的极差 ≤ limit,则相邻差一定 ≤ limit?不对!例如 [1, 3, 2] 且 limit=2,相邻差 |3-1|=2, |2-3|=1 均满足,但极差=2 也满足。但若极差 > limit,则一定不满足。因此可用极差作为剪枝条件,但核心仍是相邻差限制)。
步骤 4:正确的高效解法(滑动窗口)
由于相邻差限制是连续的,我们可以用滑动窗口维护当前窗口 [left, right]:
- 初始化
left = 0,max_len = 0。 - 遍历
right从 0 到 n-1:- 检查加入
nums[right]后,窗口内是否仍满足所有相邻差 ≤ limit(即只需检查新元素与前一元素的差)。 - 若满足,更新最大长度。
- 若不满足,则移动
left指针直到满足条件。
- 检查加入
- 时间复杂度:O(n),空间复杂度:O(1)。
举例说明
以 nums = [10, 1, 2, 4, 7, 2],limit = 5 为例:
right=0: 窗口 [10] → 长度=1right=1: 加入 1,检查 |1-10|=9 >5 → 移动 left 到 1,窗口 [1]right=2: 加入 2,|2-1|=1 ≤5 → 窗口 [1,2],长度=2right=3: 加入 4,|4-2|=2 ≤5 → 窗口 [1,2,4],长度=3right=4: 加入 7,|7-4|=3 ≤5 → 窗口 [1,2,4,7],长度=4right=5: 加入 2,|2-7|=5 ≤5 → 窗口 [1,2,4,7,2],长度=5?但需检查内部所有相邻差:|2-7|=5 满足,其他已检查过。最终最大长度为 5?不对!题目示例输出是 4,因为 [1,2,4,7] 是满足的,但加入 2 后,[2,4,7,2] 中 |7-2|=5 满足,但 [1,2,4,7,2] 中 |7-2|=5 满足,为何示例输出是 4?
重新审题:示例输入为 [10,1,2,4,7,2], limit=5,输出为 4。原因在于 [1,2,4,7] 的相邻差均 ≤5(最大差 |7-4|=3),但加入末尾的 2 后,子数组 [2,4,7,2] 中 |7-2|=5 满足,但整个窗口 [1,2,4,7,2] 中 |1-2|=1, |2-4|=2, |4-7|=3, |7-2|=5 均满足,所以长度应为 5。
查证原题示例:原题实际是 “绝对差不超过限制的最长连续子数组”,即子数组内任意两元素差的绝对值 ≤ limit(这是另一道题,我混淆了!)。但本题描述是相邻元素差。若按相邻差,则 [1,2,4,7,2] 确实满足,应输出 5。但示例输出为 4,说明我可能误读了题目。
修正题目理解
经过确认,本题实际是 “子数组内最大值与最小值的差 ≤ limit”(LeetCode 1438),而非相邻差。但你的描述明确写的是“相邻元素差”。若按相邻差,则示例输出应为 5。但为避免混淆,我按你描述的“相邻元素差”条件给出正确解法:
最终解法(相邻差限制)
- 使用滑动窗口,维护当前窗口的起始位置
left。 - 遍历数组,对于每个
right,若|nums[right] - nums[right-1]| > limit,则窗口必须从right重新开始(因为相邻差限制是连续的,一旦破坏只能重新开始)。 - 记录窗口长度的最大值。
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(1)
总结
本题通过滑动窗口可在 O(n) 时间内解决,避免了区间 DP 的 O(n²) 复杂度。关键是理解相邻差限制的连续性,允许我们通过单次遍历高效求解。