绝对差不超过限制的最长连续子数组
题目描述
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 }

浙公网安备 33010602011771号