给你一个整数数组 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 提示: 1 <= nums.length <= 10^5 1 <= nums[i] <= 10^9 0 <= limit <= 10^9
- 有序集合
- 二分法
- 滑动窗口
- 单调栈
- 暂无
这道题核心的就是求连续子数组的最大值和最小值,而由于数据是静态的,因此没必要使用线段树。
这里我们可以手动维护一个有序数组 d。其中的数据表示的就是某一个连续子数组,只不过 d 是已经排好序的。比如原有的子数组是:[3,1,2],那么 d 就是 [1,2,3]。我们可以使用二分法在
接下来,我们使用滑动窗口技巧,代码上可使用双指针。而由于 d 的长度就是窗口的大小,因此使用一个指针表示右端点即可,因为左端点可通过 右端点 - d 的长度 + 1得出。
- 维护一个有序数组,并通过二分法找到插入位置
- 语言支持:Python3
Python3 Code:
classSolution: deflongestSubarray(self, A: List[int], limit: int) ->int: d= [] ans=1fori, ainenumerate(A): bisect.insort(d, a) iflen(d) >1: whiled[-1] -d[0] >limit: d.remove(A[i-len(d)+1]) ans=max(ans, len(d)) returnans
复杂度分析
令 n 为数组长度。
- 时间复杂度:$O(n^2)$
- 空间复杂度:$O(n)$
和上面思路类似。 只不过将数据结构从数组变成了平衡树,这样插入和删除的复杂度可以降低到
代码基本也是类似的,大家自己看下即可。
- 平衡二叉树优化插入和删除的时间复杂度
- 语言支持:Python3
Python3 Code:
fromsortedcontainersimportSortedListclassSolution: deflongestSubarray(self, A: List[int], limit: int) ->int: d=SortedList() ans=1fori, ainenumerate(A): d.add(a) iflen(d) >1: whiled[-1] -d[0] >limit: d.remove(A[i-len(d)+1]) ans=max(ans, len(d)) returnans
复杂度分析
令 n 为数组长度。
- 时间复杂度:$O(nlogn)$
- 空间复杂度:$O(n)$
单调队列可快速得到最大值和最小值。因此我们可以使用两个队列,分别计算区间的最大值和最小值,接下来的思路和上面类似。即维护一个滑动窗口即可。
关于单调队列/栈可参考我之前写的文章 单调栈解题模板秒杀八道题
我们需要使用两个单调队列,一个单调增另外一个单调减。因此两个队列会存储窗口内所有的数(会有重叠),而一个单调队列显然无法做到。
比如 nums = [8,2,4,7], limit = 4,那么刚开始:
- 单调递增队列就是 q1:[8]
- 单调递减队列就是 q2:[8]
接下来:
- 单调递增队列就是 q1:[8]
- 单调递减队列就是 q2:[8,2],这个时候大于 limit,需要移除 q1,q1 变为 []
注意此时无需管 q2 内部差大于 limit
接下来:
- 单调递增队列就是 q1:[4]
- 单调递减队列就是 q2:[8,4]
接下来:
- 单调递增队列就是 q1:[4,7]
- 单调递减队列就是 q2:[8,7]
end
之所以我们不使用单调栈是因为我们需要移除左侧的数组, 因此需要在两端进行操作,这正是队列的基本操作。
- 单调队列获取最大最小值
- 语言支持:Python3
Python3 Code:
q1 是单调递减的队列,q2 是单调递增的队列。因此 q1[0] 是最大值,q2[0] 是最小值。
classSolution: deflongestSubarray(self, A: List[int], limit: int) ->int: q1, q2=collections.deque(), collections.deque() ans=1i=0forj, ainenumerate(A): whileq1andq1[-1] <a: q1.pop() q1.append(a) whileq2andq2[-1] >a: q2.pop() q2.append(a) whilei<jandq1andq2andq1[0] -q2[0] >limit: ifA[i] ==q1[0]: q1.popleft() elifA[i] ==q2[0]: q2.popleft() i+=1ans=max(ans, j-i+1) returnans
复杂度分析
令 n 为数组长度。
- 时间复杂度:$O(n)$
- 空间复杂度:$O(n)$
此题解由 力扣刷题插件 自动生成。
力扣的小伙伴可以关注我,这样就会第一时间收到我的动态啦~
以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。
关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。