跳到主要内容

1 篇博文 含有标签「longestSubarray」

查看所有标签

力扣1438-绝对差不超过限制的最长连续子数组

· 阅读需 7 分钟
Yana Ching
Front End Engineer

原题


给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。

如果不存在满足条件的子数组,则返回 0 。



示例 1:

输入:nums = [8,2,4,7], limit = 4
输出:2
解释:所有子数组如下:
[8] 最大绝对差 |8-8| = 0 <= 4.
[8,2] 最大绝对差 |8-2| = 6 > 4.
[8,2,4] 最大绝对差 |8-2| = 6 > 4.
[8,2,4,7] 最大绝对差 |8-2| = 6 > 4.
[2] 最大绝对差 |2-2| = 0 <= 4.
[2,4] 最大绝对差 |2-4| = 2 <= 4.
[2,4,7] 最大绝对差 |2-7| = 5 > 4.
[4] 最大绝对差 |4-4| = 0 <= 4.
[4,7] 最大绝对差 |4-7| = 3 <= 4.
[7] 最大绝对差 |7-7| = 0 <= 4.
因此,满足题意的最长子数组的长度为 2 。
示例 2:

输入:nums = [10,1,2,4,7,2], limit = 5
输出:4
解释:满足题意的最长子数组是 [2,4,7,2],其最大绝对差 |2-7| = 5 <= 5 。
示例 3:

输入:nums = [4,2,2,2,4,4,2,2], limit = 0
输出:3


提示:

1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
0 <= limit <= 10^9

分析

  1. 求连续子数组,考虑滑动窗口
  2. 求差值考虑连续区间内的最大、最小值[每次更新极值会把不符合的数据都清除出去]
  3. 窗口不符合时,检查极端值是否包含区间值,如果是,移除这个极端值队头

解题思路


整理题目: 数组 nums,连续元素间的绝对值 <= limit,要求返回符合的窗口长度(max)

- 👉 绝对值考虑区间的最大最小值
- 👉 两个队列来分别记录区间拥有的最大、最小值
- 👉 没有长度限制,因此更新绝对值没有限定条件,每次移动右指针都需要更新
- 关注:何时收缩左指针
- 👉 一旦绝对值 > limit 就需要收缩左指针

- 收缩左指针注意事项:

判断左指针对应元素是否在两个队列的队头,如果在,将队列头部出队(单调队列只关注队头,nums[l] 出 现在队中或者队尾,一旦有
新的更符合条件的元素入队,随时会被出队,因此不关注)

1. 遍历整个 nums,循环结束条件是 r < nums.length
2. 判断极值队列中不为空,将队列尾部大于/小于 nums[r] 的元素移除出队
3. 分别将 nums[r] 压入队列
4. while 循环设置收缩左指针的逻辑 Math.abs(max-min) > k 时,收缩左指针 l+【收缩之前查看即将被排除的 nums[l] 是否在极值队
列的队头影响结果,如果在,将队列队头元素出队】

伪代码

nums = [8, 2, 4, 7]
limit = 4
rightnums[right]maxDequeminDequemax-minleftres当前窗口
08[8][8]001[8]
12[8,2][2]611[2]
24[4][2,4]212[2,4]
37[7][2,4,7] → [2,4]522[4,7]

代码

/**
* @param {number[]} nums
* @param {number} limit
* @return {number}
*/
var longestSubarray = function (nums, limit) {
/**
整理题目:
数组 nums,连续元素间的绝对值 <= limit,要求返回符合的窗口长度(max)
👉 绝对值考虑区间的最大最小值
👉 两个队列来分别记录区间拥有的最大、最小值
👉 没有长度限制,因此更新绝对值没有限定条件,每次移动右指针都需要更新
关注:何时收缩左指针 👉 一旦绝对值 > limit 就需要收缩左指针
收缩左指针注意事项:
判断左指针对应元素是否在两个队列的队头,如果在,将队列头部出队(单调队列只关注队头,nums[l] 出现在队中或者队尾,一旦有新的更符合条件的元素入队,随时会被出队,因此不关注)

1. 遍历整个 nums,循环结束条件是 r < nums.length
2. 判断极值队列中不为空,将队列尾部大于/小于 nums[r] 的元素移除出队
2. 分别将 nums[r] 压入队列
3. while 循环设置收缩左指针的逻辑 Math.abs(max-min) > k 时,收缩左指针 l+【收缩之前查看即将被排除的 nums[l] 是否在极值队列的队头影响结果,如果在,将队列队头元素出队】
*/
let l = 0
let r = 0
let queueMax = []
let queueMin = []

let res = 0
while (r < nums.length) {
while (queueMax.length && nums[r] > queueMax[queueMax.length - 1]) {
queueMax.pop()
}
queueMax.push(nums[r])

while (queueMin.length && nums[r] < queueMin[queueMin.length - 1]) {
queueMin.pop()
}

queueMin.push(nums[r])

// l 收缩逻辑
if (queueMax.length && queueMin.length && Math.abs(queueMax[0] - queueMin[0]) > limit) {
// 收缩之前检查(检查一次就行,不需要用 while)
if (queueMax[0] == nums[l]) {
queueMax.shift()
}
if (queueMin[0] == nums[l]) {
queueMin.shift()
}

// 收缩区间
l++
}

res = Math.max(r - l + 1, res)
r++
}
console.log({ res })
return res
}

推荐关联(easy -> hard)

  • 1004 基础滑动窗口
  • 239 单调队列
  • 1438 滑动窗口 + 单调队列
  • 1696/862 单调队列优化 DP
  • 480 中位数窗口复杂的数据结构维护