【数据结构与算法】双端队列 deque
参考《程序员面试指南:IT名企算法于数据结构题目最优解(第2版)》
双端队列deque
普通的队列是先入先出FIFO(First In First Out),只能从队尾添加元素,从队首弹出元素。而双端队列则是从队尾添加元素,但是既可以从队首弹出元素,和普通的队列一样,也可以和栈一样从队尾弹出元素,所以叫双端队列deque(double-ended queue),它同时具备队列(FIFO)和栈(LIFO)的特性。
生成窗口最大值数组
题目描述
有一个整型数组
arr
和一个大小为w
的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。如果数组长度为n
,窗口大小为w
,则一共产生n-w+1
个窗口的最大值。请实现一个函数。
● 输入:整型数组arr
,窗口大小为w
。
● 输出:一个长度为n-w+1
的数组res
,res[i]
表示每一种窗口状态下的最大值。
示例:
输入:arr = [4,3,5,4,3,3,6,7]
,w = 3
输出:res = [5,5,5,4,6,7]
暴力解法
时间复杂度为$\mathcal{O}(N\times w)$,遍历arr
,每次统计w
窗口中的最大值即可。
双端队列解法
时间复杂度为$\mathcal{O}(N)$。
大致思路是维护一个从队首到队尾单调递减的双端队列qmax
,其中存放着arr
中的下标,还有一个res
数组。
遍历到arr[i]
时qmax
的更新规则为:
- 如果
qmax
为空,则直接将i
放入队尾; - 如果
qmax
不为空,则需要判断队尾元素j
对应arr[j]
与当前元素的大小:
1). 如果arr[j] <= arr[i]
,则弹出j
,继续更新规则;
2). 如果arr[j] > arr[i]
,则直接将i
添加进队尾。
3. 如果qmax[0] == i - w
,则说明队首元素不再位于w
窗口内,则弹出队首元素。
4. 如果i >= w - 1
,则向res
添加arr[qmax[0]]
。
1 | arr = [4, 3, 5, 4, 3, 3, 6, 7] |
最大值减去最小值差值小于等于num的子数组数量
题目描述
给定数组
arr
和整数num
,返回一共有多少个子数组满足如下情况:max(arr[i..j]) - min(arr[i..j]) <= num
示例:
输入:arr = [4,3,5,4,3,3,6,7]
,num = 1
输出:ans = 14
暴力解法
时间复杂度为$\mathcal{O}(N^3)$,首先arr
一共有$\mathcal{O}(N^2)$个子数组,然后找出每个子数组的最小值和最大值,再判断是否满足条件$\mathcal{O}(N)$。
双端队列解法
时间复杂度为$\mathcal{O}(N)$。
大致思路是维护两个双端队列qmax
和qmin
,分别是从队首到队尾递减和递增,qmax
队首表示当前子数组arr[i..j]
最大值,qmin
队首表示当前子数组arr[i..j]
最小值,i
和j
都是从0
开始的两个指针,i
表示子数组左边界,j
表示子数组右边界,ans
代表最终答案,初始为0
。
- 遍历到
arr[j]
时,更新qmax
和qmin
,更新规则和上一题一样; - 重复第一步直到
arr[qmax[0]] - arr[qmin[0]] > num
,则j
指针暂停移动,ans += j - i
(j - i
表示严格以i
起始的满足条件的子数组数量),然后如判断qmax
和qmin
的队首元素是否为i,如果是则弹出i
,之后将i
指针向后移动一位,重复一二步。
1 | arr = [4, 3, 5, 4, 3, 3, 6, 7] |