旺崽的博客

要么天赋异禀,要么天道酬勤

0%

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

题目链接

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

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

示例 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
输入: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:

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

示例 3:

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

双指针~
我们开设两个指针 $l,r$,先移动 $r$ 指针,并记录区间 $[l,r]$ 内的最大值 $mx$ 和最小值 $mn$,显然当 $mx-mn\leq limit$ 时,区间长度就是答案;当条件不满足时,我们只需要固定 $r$ 指针,移动 $l$ 指针即可,即必须缩短区间长度以满足题目所给条件,但是我们移动指针的过程中要同时改变最大最小值,显然光开两个变量是不够的,我提供了两种方法:

  • 开一个大根堆 $mx$ 和小根堆 $mn$,显然堆顶就是我们要找的最大最小值,维护两个堆即可
  • C++里面有一个强大的数据结构 $multiset$(同样是最容易被忽视的数据结构),这个数据结构自动排序且支持元素重复,显然 $multiset$ 的迭代器指向的开头和末尾就是我们要找的最大最小值

下面两段代码分别对应第一种和第二种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
int longestSubarray(vector<int> &nums, int limit) {
int l = 0, r = 0, ans = 0;
unordered_map<int, int> cnt;
priority_queue<int, vector<int>, less<>> mx;
priority_queue<int, vector<int>, greater<>> mn;
while (r < nums.size()) {
cnt[nums[r]]++;
mx.emplace(nums[r]);
mn.emplace(nums[r]);
while (mx.top() - mn.top() > limit) {
cnt[nums[l]]--;
while (!cnt[mx.top()]) mx.pop();
while (!cnt[mn.top()]) mn.pop();
l++;
}
ans = max(ans, r - l + 1);
r++;
}
return ans;
}
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
int longestSubarray(vector<int> &nums, int limit) {
int l = 0, r = 0, ans = 0;
multiset<int> temp;
while (r < nums.size()) {
temp.emplace(nums[r]);
while (*temp.rbegin() - *temp.begin() > limit) {
temp.erase(temp.find(nums[l++]));
}
ans = max(ans, r - l + 1);
r++;
}
return ans;
}
};