echo

任生命穿梭 时间的角落

0%

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

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

提示:

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

方法一:暴力法(超时)

直接枚举子数组的开始位置和结束位置,同时维护最大最小值,并判断该子数组是否满足条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public int longestSubarray(int[] nums, int limit) {
int n = nums.length;
int ans = 0;
//枚举每一个子数组nums[i ... j]
for(int i = 0; i < n; i++){
//初始化子数组中最大最小值
int min = nums[i], max = nums[i];
for(int j = i; j < n; j++){//对于每个子数组,更新最大最小值
if(nums[j] > max){
max = nums[j];
}else if(nums[j] < min){
min = nums[j];
}
//判断子数组是否满足条件并维护结果
if(max - min <= limit && j - i + 1 > ans){
ans = j - i + 1;
}
}

}
return ans;
}
}
  • 时间复杂度O(n^2)
  • 空间复杂度O(1)

方法二:滑动窗口+单调队列

枚举每一个位置作为右端点,找到其对应的最靠左的左端点,满足区间中最大与最小值不超过 limit。我们分别使用两个单调队列保存最大和最小值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public int longestSubarray(int[] nums, int limit) {
Deque<Integer> queMax = new LinkedList<Integer>();
Deque<Integer> queMin = new LinkedList<Integer>();
int n = nums.length;
int left = 0, right = 0;
int ret = 0;
//枚举每一个右端点 right
while (right < n) {
while (!queMax.isEmpty() && queMax.peekLast() < nums[right]) {
queMax.pollLast();
}
while (!queMin.isEmpty() && queMin.peekLast() > nums[right]) {
queMin.pollLast();
}

queMax.offerLast(nums[right]);
queMin.offerLast(nums[right]);
//当 nums[left ... right]不满足条件时将 left 右移
while (!queMax.isEmpty() && !queMin.isEmpty() && queMax.peekFirst() - queMin.peekFirst() > limit) {
if (nums[left] == queMin.peekFirst()) {
queMin.pollFirst();
}
if (nums[left] == queMax.peekFirst()) {
queMax.pollFirst();
}
left++;
}
ret = Math.max(ret, right - left + 1);
right++;
}
return ret;
}
}
  • 时间复杂度O(n)
  • 空间复杂度O(n)

方法三:滑动窗口+有序集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int longestSubarray(int[] nums, int limit) {
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
int n = nums.length;
int left = 0, right = 0;
int ret = 0;
while (right < n) {
map.put(nums[right], map.getOrDefault(nums[right], 0) + 1);
while (map.lastKey() - map.firstKey() > limit) {
map.put(nums[left], map.get(nums[left]) - 1);
if (map.get(nums[left]) == 0) {
map.remove(nums[left]);
}
left++;
}
ret = Math.max(ret, right - left + 1);
right++;
}
return ret;
}
}