题目描述
给你一个整数数组 nums
,和一个表示限制的整数 limit
,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。
如果不存在满足条件的子数组,则返回 0
。
样例
输入: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 。
输入:nums = [10,1,2,4,7,2], limit = 5
输出:4
解释:满足题意的最长子数组是 [2,4,7,2],其最大绝对差 |2-7| = 5 <= 5 。
输入: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、
check
函数中,给定滑动窗口的长度mid
,若存在某一个滑动窗口中的最大值 - 滑动窗口中的最小值 <= limit,则返回true
,否则返回false
,注意:比较滑动窗口的最大最小值,需要从mid - 1
位置开始比较 - 3、以
i
结尾的nums[i]
,滑动窗口的区间是[i - x + 1,i]
,两个单调队列分别维护的是该区间的最大值和最小值,由于滑动窗口包含i
,因此 滑动窗口中的最大值 - 滑动窗口中的最小值 操作时,需要在while
下方进行更新
时间复杂度 $O(nlogn)$
Java 代码
class Solution {
static boolean check(int mid,int[] nums,int limit)
{
int n = nums.length;
int hh1 = 0,tt1 = -1;
int hh2 = 0,tt2 = -1;
int[] q1 = new int[n + 10];
int[] q2 = new int[n + 10];
for(int i = 0;i < n;i ++)
{
//最大值
if(hh1 <= tt1 && q1[hh1] < i - mid + 1) hh1 ++;
while(hh1 <= tt1 && nums[q1[tt1]] <= nums[i]) tt1 --;
q1[++ tt1] = i;
//最小值
if(hh2 <= tt2 && q2[hh2] < i - mid + 1) hh2 ++;
while(hh2 <= tt2 && nums[q2[tt2]] >= nums[i]) tt2 --;
q2[++ tt2] = i;
if(i >= mid - 1 && nums[q1[hh1]] - nums[q2[hh2]] <= limit) return true;
}
return false;
}
public int longestSubarray(int[] nums, int limit) {
int l = 0,r = 1000000000;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(check(mid,nums,limit)) l = mid;
else r = mid - 1;
}
return l;
}
}
我就想到单调队列
二分做法倒是没怎么考虑 ,赞一个
谢谢hh