数据结构入门-单调队列
原理介绍
双向队列
![双向队列值示意图](https://i-blog.csdnimg.cn/blog_migrate/5cb6a6113a8390666e4fc30c471e15e5.png)
思考一下:对于数组nums
,我们想知道max(nums[i-k],...,nums[i])
如何高效处理?
![双向队列值示意图](https://i-blog.csdnimg.cn/blog_migrate/5c751fc19536e8e39f61e6ebc55b1d01.png)
单调队列
单调队列,即从队首到队尾单调的队列。
![单调队列流程图](https://i-blog.csdnimg.cn/blog_migrate/75d88420565c5aa7e532aa452d0cc264.png)
假设nums = [10,8,6,4,5,12],k=2
,单调队列滑动窗口入队出队过程:
![](https://i-blog.csdnimg.cn/blog_migrate/8583205e39ce7732176c2852eefa9aca.png)
需要注意的是在滑动窗口时,我们需要知道原数据节点的下标,所以需要以二元组绑定索引值的形式处理。这种情况下,队首始终是最大值,并满足从队首到队尾单调递减。
例题练习
力扣 239. 滑动窗口最大值
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
int[] ans = new int[n-k+1];
Deque<Integer[]> ql = new ArrayDeque<Integer[]>();
for (int i = 0; i < n; i++) {
while (!ql.isEmpty() && i-ql.peekFirst()[0] >= k) ql.pollFirst();
Integer[] t = {i, nums[i]};
while (!ql.isEmpty() && ql.peekLast()[1] < nums[i]) ql.pollLast();
ql.offerLast(t);
if (i-k+1 >= 0) ans[i-k+1] = ql.peekFirst()[1];
}
return ans;
}
}
Go语言手写数据结构
// 链表节点
type Node struct {
Idx int
Val int
Left *Node
Right *Node
}
// 双向链表
type Deque struct {
First *Node
Last *Node
}
// 判断队空
func (this Deque) IsEmpty() bool {
return this.First == nil
}
// 队首值
func (this Deque) getFirst() int {
return this.First.Val
}
// 队尾值
func (this Deque) getLast() int {
return this.Last.Val
}
// 队首下标
func (this Deque) FirstIndex() int {
return this.First.Idx
}
// 队尾下标
func (this Deque) LastIndex() int {
return this.Last.Idx
}
// 队首入队
func (this *Deque) OfferFirst(idx int, val int) bool {
if this.IsEmpty() {
this.First = &Node{Idx: idx, Val: val}
this.Last = this.First
return true
}
this.First.Left = &Node{Idx: idx, Val: val, Right: this.First}
this.First = this.First.Left
return true
}
// 队尾入队
func (this *Deque) OfferLast(idx int, val int) bool {
if this.IsEmpty() {
this.First = &Node{Idx: idx, Val: val}
this.Last = this.First
return true
}
this.Last.Right = &Node{Idx: idx, Val: val, Left: this.Last}
this.Last = this.Last.Right
return true
}
// 队首出队
func (this *Deque) PollFirst() *Node {
if this.IsEmpty() {
return nil
}
res := this.First
if this.First == this.Last {
this.First = nil
this.Last = nil
return res
}
this.First = this.First.Right
this.First.Left = nil
return res
}
// 队尾出队
func (this *Deque) PollLast() *Node {
if this.IsEmpty() {
return nil
}
res := this.Last
if this.First == this.Last {
this.First = nil
this.Last = nil
return res
}
this.Last = this.Last.Left
this.Last.Right = nil
return res
}
func maxSlidingWindow(nums []int, k int) []int {
n := len(nums)
ans := make([]int, n-k+1)
var ql Deque
for i, v := range nums {
// 维护窗口大小
for !ql.IsEmpty() && i-ql.FirstIndex() >= k {
ql.PollFirst()
}
// 维护单调队列
for !ql.IsEmpty() && ql.getLast() < v {
ql.PollLast()
}
ql.OfferLast(i, v)
if i-k+1 >= 0 {
ans[i-k+1] = ql.getFirst()
}
}
return ans
}
P1886 滑动窗口 /【模板】单调队列
70分~,这道题还是建议使用C++,否则很容易内存超出(MLE)或者时间超出(TLE)。
import java.io.*;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Scanner;
public class Main {
static int[] minSlidingWindow(int[] nums, int k) {
int n = nums.length;
int[] ans = new int[n-k+1];
Deque<Integer[]> ql = new ArrayDeque<Integer[]>();
for (int i = 0; i < n; i++) {
while (!ql.isEmpty() && i-ql.peekFirst()[0] >= k) ql.pollFirst();
Integer[] t = {i, nums[i]};
while (!ql.isEmpty() && ql.peekLast()[1] > nums[i]) ql.pollLast();
ql.offerLast(t);
if (i-k+1 >= 0) ans[i-k+1] = ql.peekFirst()[1];
}
return ans;
}
static int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
int[] ans = new int[n-k+1];
Deque<Integer[]> ql = new ArrayDeque<Integer[]>();
for (int i = 0; i < n; i++) {
while (!ql.isEmpty() && i-ql.peekFirst()[0] >= k) ql.pollFirst();
Integer[] t = {i, nums[i]};
while (!ql.isEmpty() && ql.peekLast()[1] < nums[i]) ql.pollLast();
ql.offerLast(t);
if (i-k+1 >= 0) ans[i-k+1] = ql.peekFirst()[1];
}
return ans;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
int n = sc.nextInt();
int k = sc.nextInt();
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = sc.nextInt();
}
int[] ans1 = minSlidingWindow(nums, k);
int[] ans2 = maxSlidingWindow(nums, k);
for (int i = 0; i < n-k+1; i++) {
out.print(ans1[i]);
if (i != n-k) out.print(" ");
}
out.println();
out.flush();
for (int i = 0; i < n-k+1; i++) {
out.print(ans2[i]);
if (i != n-k) out.print(" ");
}
out.flush();
}
}