【题解】力扣1438. 绝对差不超过限制的最长连续子数组
1438. 绝对差不超过限制的最长连续子数组
题目来源
思路
方法一
滑动窗口+有序集合
使用滑动窗口保持符合条件的子数组,记录最长长度。
使用平衡数,统计当前窗口内的最大值和最小值。
- 使用
left
和right
两个指针,分别指向滑动窗口的左右两边; right
主动右移,left
被动右移。当滑动窗口内的最大值与最小值之差大于limit
时,left
被迫向右移动,直到窗口内的最大值和最小值之差小于limit
;每次left
向右移动时,被移除窗口的数值的次数都要减一。- 用一个数
res
维护滑动窗口的长度的最大值。
class Solution {
public int longestSubarray(int[] nums, int limit) {
TreeMap<Integer, Integer> map = new TreeMap<>();
// 平衡二叉树
int left = 0, right = 0;
int res = 0;
while (right < nums.length) {
map.put(nums[right], map.getOrDefault(nums[right], 0) + 1);
// map.put(key, value)
// map.getOrDefault(value, default),如果有值,就取值,没值,就取默认值(默认值自己设定的
// map.lastKey(),取最后的Key,就是最大的值
// map.firstKey(),取最前面的Key,就是最小的值
while (map.lastKey() - map.firstKey() > limit) {
// 下面这一行是让nums[left]的value值减一
map.put(nums[left], map.get(nums[left]) - 1);
if (map.get(nums[left]) == 0) {
map.remove(nums[left]);
}
left ++;
}
res = Math.max(res, right - left + 1);
right ++;
}
return res;
}
}
方法二
滑动窗口+单调队列
在方法一中,我们仅需记录当前窗口内的最大值和最小值,因此可以分别用两个单调队列解决问题。
使用一个单调递减的队列maxque来维护窗口内的最大值,使用一个单调递增的队列minque来维护窗口内的最小值。这样只需要计算两个队列的队首之差,就可以判断单前窗口是否满足条件了。
class Solution {
public int longestSubarray(int[] nums, int limit) {
int len = nums.length;
Deque<Integer> maxque = new LinkedList<>();
Deque<Integer> minque = new LinkedList<>();
int left = 0, right = 0;
int res = 0;
while(right < len){
// 队列不为空,且队尾元素小于滑动窗口最右边的值时(不是递减队列),弹出队尾元素
while(!maxque.isEmpty() && maxque.peekLast() < nums[right]){
maxque.pollLast();
}
// 队列不为空,且不是递增队列时,弹出队尾元素
while(!minque.isEmpty() && minque.peekLast() > nums[right]){
minque.pollLast();
}
maxque.offerLast(nums[right]);
minque.offerLast(nums[right]);
// 计算两个单调队列的队首元素之差是否满足条件
while(!maxque.isEmpty() && !minque.isEmpty() && maxque.peekFirst() - minque.peekFirst() > limit){
if(nums[left] == maxque.peekFirst()){
maxque.pollFirst();
}
if(nums[left] == minque.peekFirst()){
minque.pollFirst();
}
left++;
}
// 更新窗口长度
res = Math.max(res, right-left+1);
right++;
}
return res;
}
}