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

题目描述

1438. 绝对差不超过限制的最长连续子数组 难度 中等

给你一个整数数组 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

 题解

任意两个元素之间的绝对差必须小于或者等于 limit 可以转化为选定区间内最大值和最小值的绝对值小于等于limit。因此,题目转化为求最大值和最小值的绝对值小于等于limit的最大连续子数组的长度。

连续子数组问题,容易想到滑动窗口(虫取法)

最初思路

代码:

 1 class Solution {
 2     public int longestSubarray(int[] nums, int limit) {
 3         int minNum = Integer.MAX_VALUE;
 4         int minIndex = -1;
 5         int maxNum = Integer.MIN_VALUE;
 6         int longest = 0;
 7         int maxIndex = -1;
 8         int i,j;
 9         for(i=0,j=0;j < nums.length;){
10             if(nums[j]<minNum){
11                 minNum = nums[j];
12                 minIndex = j;
13             }
14             if(nums[j]>maxNum){
15                 maxNum = nums[j];
16                 maxIndex = j;
17             }
18             if(Math.abs(maxNum - minNum) <= limit){//满足条件,窗口右端j右滑
19                 j++;
20             }else{
21                 longest = Math.max(longest,j - i);
22                 i = Math.min(minIndex,maxIndex) + 1;//不满足条件,窗口左端i右滑
23                 j = i;
24                 maxNum = Integer.MIN_VALUE;
25                 minNum = Integer.MAX_VALUE;
26             }
27         }
28         longest = Math.max(longest,j - i);
29         return longest;
30     }
31 }
58 / 60 个通过测试用例   时间超限  第59个用例过长,初始思路优化程度达不到题目要求。
时间超限主要是因为j的回退。
因为i右移后,区间最大、最小值发生变化,需要重新寻找。

改进的尝试(待以后补充)

代码:

 1 class Solution {
 2     public int longestSubarray(int[] nums, int limit) {
 3         int minNum = Integer.MAX_VALUE;
 4         int minIndex = -1;
 5         int maxNum = Integer.MIN_VALUE;
 6         int maxIndex = -1;
 7         int sminNum = Integer.MAX_VALUE;
 8         int sminIndex = -1;
 9         int smaxNum = Integer.MIN_VALUE;
10         int smaxIndex = -1;
11         int longest = 0;
12         int i,j;
13         for(i=0,j=0;j < nums.length&&i <= j;){
14             if(nums[j]<minNum){
15                 if(minNum<sminNum){
16                     sminNum = minNum;
17                     sminIndex = minIndex;
18                 }
19                 minNum = nums[j];
20                 minIndex = j;
21             }else if(nums[j]<sminNum){
22                 sminNum = nums[j];
23                 sminIndex = j;
24             }
25             if(nums[j]>maxNum){
26                 if(maxNum>smaxNum){
27                     smaxNum = maxNum;
28                     smaxIndex = maxIndex;
29                 }
30                 maxNum = nums[j];
31                 maxIndex = j;
32             }else if(nums[j]>smaxNum){
33                 smaxNum = nums[j];
34                 smaxIndex = j;
35             }
36             if(Math.abs(maxNum - minNum) <= limit){
37                 j++;
38             }else{
39                 longest = Math.max(longest,j - i);
40                 i = Math.min(minIndex,maxIndex);
41                 if(i == minIndex){
42                     minNum = sminNum;
43                     minIndex = sminIndex;
44                     sminNum = Integer.MAX_VALUE;
45                     sminIndex = -1;
46                 }else{
47                     maxNum = smaxNum;
48                     maxIndex = smaxIndex;
49                     smaxNum = Integer.MIN_VALUE;
50                     smaxIndex = -1;
51                 }
52                 i += 1;
53             }
54         }
55         longest = Math.max(longest,j - i);
56         return longest;
57     }
58 }

我想要记录最大、次最大、最小、次最小值及其位置,以便i右滑时,j不回退,次最大值就变成了最大值(或者次最小值就变成最小值)。但最终没有成功。

红黑树解决

单调队列

 用单调队列记录最大值、最小值,可以避免j的回溯,实现最大最小值的自动更新。

 1 class Solution {
 2     public int longestSubarray(int[] nums, int limit) {
 3         Deque<Integer> min = new LinkedList<Integer>();//递增序列,队首为最小值
 4         Deque<Integer> max = new LinkedList<Integer>();//递减序列,队首为最大值
 5         int longest = 0;
 6         int n = nums.length;
 7         int left = 0;
 8         int right = 0;
 9         while(right < n){
10             while(!min.isEmpty()&&nums[right]<min.peekLast()){//维护最小值递增序列,新加入的不能小于队尾元素
11                 min.pollLast();
12             }
13             while(!max.isEmpty()&&nums[right]>max.peekLast()){//维护最大值递减序列,新加入的不能大于队尾元素
14                 max.pollLast();
15             }
16             min.offerLast(nums[right]);
17             max.offerLast(nums[right]);
18             while(!min.isEmpty()&&!max.isEmpty()&&max.peekFirst()-min.peekFirst()>limit){
19                 if(nums[left]==min.peekFirst()){//如果不满足条件的区间,左端恰是最小值
20                     min.pollFirst();//移除这个最小值
21                 }                
22                 if(nums[left]==max.peekFirst()){//如果不满足条件的区间,左端恰是最大值
23                     max.pollFirst();//移除这个最大值
24                 }   
25                 left++;//如果不满足条件,left左移,无论左端是否是区间最大、最小值
26             }
27             longest = Math.max(longest,right-left+1);  //左右[left,right]闭区间
28             right++;
29         }
30         return longest;
31     }
32 }

 

posted @ 2021-02-22 18:36  HickeyZhang  阅读(231)  评论(0)    收藏  举报