1. 概述
双端队列、队列、栈对比
定义 | 特点 | |
队列 | 一端删除(头),另一端添加(尾) | First In First Out |
栈 | 一端删除和添加(顶) | Last In First Out |
双端队列 | 两端都可以删除、添加 | |
优先级队列 | 优先级高者先出队 | |
延时队列 | 根据延时时间确定优先级 | |
并发非阻塞队列 | 队列空或满时不阻塞 | |
并发阻塞队列 | 队列空时删除阻塞、队列满时添加阻塞 |
注1:Jav中LinkedList即为典型双端队列实现,不过它同时实现了Queue接口,也提供了栈的push、pop等方法。
注2:不同语言,操作双端队列的方法命名有所不同,参见下表
操作 | Java | JavaScript | C++ | LeetCode 641 |
尾部插入 | offerLast | push | push_back | insertLast |
头部插入 | offerFirst | unshift | push_front | insertFront |
尾部移除 | pollLast | pop | pop_back | deleteLast |
头部移除 | pollFirst | shift | pop_front | deleteFront |
尾部获取 | peekLast | at(-1) | back | getRear |
头部获取 | peekFirst | at(0) | front | getFront |
接口定义
package com.itheima.datastructure.deque;
/**
* 双端队列接口
* @param <E> 队列中元素类型
*/
public interface Deque<E> {
boolean offerFirst(E e);
boolean offerLast(E e);
E pollFirst();
E pollLast();
E peekFirst();
E peekLast();
boolean isEmpty();
boolean isFull();
}
2. 基于双向环形链表实现
package com.itheima.datastructure.Deque;
import java.util.Iterator;
public class LinkedListDeque<E> implements Deque<E>, Iterable<E> {
static class Node<E> {
Node<E> prev;
E value;
Node<E> next;
public Node(Node<E> prev, E value, Node<E> next) {
this.prev = prev;
this.value = value;
this.next = next;
}
}
int capacity;
int size;
Node<E> sentinel = new Node<>(null,null, null);
public LinkedListDeque(int capacity) {
this.capacity = capacity;
sentinel.next = sentinel;
sentinel.prev = sentinel;
}
@Override
public boolean offerFirst(E e) {
if(isFull()) {
return false;
}
// 处理4个指针
Node<E> a = sentinel;
Node<E> b = sentinel.next;
Node<E> added = new Node<>(a, e, b);
a.next = added;
b.prev = added;
size++;
return true;
}
// a added b
@Override
public boolean offerLast(E e) {
if(isFull()) {
return false;
}
Node<E> b = sentinel;
Node<E> a = sentinel.prev;
Node<E> added = new Node<>(a, e, b);
a.next = added;
b.prev = added;
size++;
return true;
}
@Override
public E pollFirst() {
if(isEmpty()) {
return null;
}
Node<E> a = sentinel;
Node<E> removed = sentinel.next;
Node<E> b = sentinel.next.next;
a.next = b;
b.prev = a;
size--;
return removed.value;
}
@Override
public E pollLast() {
if(isEmpty()) {
return null;
}
Node<E> a = sentinel.prev.prev;
Node<E> removed = sentinel.prev;
Node<E> b = sentinel;
a.next = b;
b.prev = a;
size--;
return removed.value;
}
@Override
public E peekFirst() {
if(isEmpty()) {
return null;
}
return sentinel.next.value;
}
@Override
public E peekLast() {
if(isEmpty()) {
return null;
}
return sentinel.prev.value;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean isFull() {
return size == capacity;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
Node<E> p = sentinel;
@Override
public boolean hasNext() {
return p != sentinel;
}
@Override
public E next() {
E value = p.value;
p = p.next;
return value;
}
};
}
}
3. 基于循环数组实现
package com.itheima.datastructure.Deque;
import java.util.Iterator;
public class ArrayDeque1<E> implements Deque<E>, Iterable<E>{
E[] array;
int head;
int tail;
public ArrayDeque1(int capacity) {
array = (E[]) new Object[capacity + 1]; // 留一个空位置
}
static int inc(int i, int length) {
if(i + 1 >= length) {
return 0;
}
return i + 1;
}
static int dec(int i, int length) {
if(i - 1 < 0) {
return length - 1;
}
return i - 1;
}
@Override
public boolean offerFirst(E e) {
if(isFull()) {
return false;
}
// head先往前一个位置,再存放元素
head = dec(head, array.length);
array[head] = e;
return true;
}
@Override
public boolean offerLast(E e) {
if(isFull()) {
return false;
}
// 先存放元素到tail所在位置,再将tail往后一个位置
array[tail] = e;
tail = inc(tail, array.length);
return true;
}
@Override
public E pollFirst() {
if(isEmpty()) {
return null;
}
// 先获取head位置的元素,然后将其置空,并将head往后移一个位置
E e = array[head];
array[head] = null;
head = inc(head, array.length);
return e;
}
@Override
public E pollLast() {
if(isEmpty()) {
return null;
}
// 先将tail的位置往前移一位,获取tail位置的元素并将其置空
tail = dec(tail, array.length);
E e = array[tail];
array[tail] = null;
return e;
}
@Override
public E peekFirst() {
if(isEmpty()) {
return null;
}
return array[head];
}
@Override
public E peekLast() {
if(isEmpty()) {
return null;
}
return array[tail - 1];
}
@Override
public boolean isEmpty() {
return head == tail;
}
@Override
public boolean isFull() {
if(tail > head) {
return tail - head == array.length - 1;
} else if(tail < head) {
return head - tail == 1;
} else {
return false;
}
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
int p = head;
@Override
public boolean hasNext() {
return p != tail;
}
@Override
public E next() {
E e = array[p];
p = inc(p, array.length);
return e;
}
};
}
}
4. 习题
4.1 二叉树Z字层序遍历
给你二叉树的根节点 root
,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:[[3],[20,9],[15,7]]
示例 2:
输入:root = [1] 输出:[[1]]
示例 3:
输入:root = [] 输出:[]
提示:
- 树中节点数目在范围
[0, 2000]
内 -100 <= Node.val <= 100
解法一:双端队列
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) {
return result;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(root); // 根节点入队
boolean odd = true; // 判断是否是奇数行
int c1 = 1; // 当前层的结点数
while (!queue.isEmpty()) {
int c2 = 0;
LinkedList<Integer> deque = new LinkedList<>(); // 存储当前层
for (int i = 0; i < c1; i++) {
TreeNode n = queue.poll();
if (odd) {
deque.offerLast(n.val); // 尾插
} else {
deque.offerFirst(n.val); // 头插
}
if (n.left != null) {
queue.offer(n.left);
c2++;
}
if (n.right != null) {
queue.offer(n.right);
c2++;
}
}
c1 = c2;
odd = !odd;
result.add(deque);
}
return result;
}
}
4.2 设计双端循环队列
设计实现双端队列。
实现 MyCircularDeque
类:
MyCircularDeque(int k)
:构造函数,双端队列最大为k
。boolean insertFront()
:将一个元素添加到双端队列头部。 如果操作成功返回true
,否则返回false
。boolean insertLast()
:将一个元素添加到双端队列尾部。如果操作成功返回true
,否则返回false
。boolean deleteFront()
:从双端队列头部删除一个元素。 如果操作成功返回true
,否则返回false
。boolean deleteLast()
:从双端队列尾部删除一个元素。如果操作成功返回true
,否则返回false
。int getFront()
):从双端队列头部获得一个元素。如果双端队列为空,返回-1
。int getRear()
:获得双端队列的最后一个元素。 如果双端队列为空,返回-1
。boolean isEmpty()
:若双端队列为空,则返回true
,否则返回false
。boolean isFull()
:若双端队列满了,则返回true
,否则返回false
。
示例 1:
输入 ["MyCircularDeque", "insertLast", "insertLast", "insertFront", "insertFront", "getRear", "isFull", "deleteLast", "insertFront", "getFront"] [[3], [1], [2], [3], [4], [], [], [], [4], []] 输出 [null, true, true, true, false, 2, true, true, true, 4] 解释 MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3 circularDeque.insertLast(1); // 返回 true circularDeque.insertLast(2); // 返回 true circularDeque.insertFront(3); // 返回 true circularDeque.insertFront(4); // 已经满了,返回 false circularDeque.getRear(); // 返回 2 circularDeque.isFull(); // 返回 true circularDeque.deleteLast(); // 返回 true circularDeque.insertFront(4); // 返回 true circularDeque.getFront(); // 返回 4
提示:
1 <= k <= 1000
0 <= value <= 1000
insertFront
,insertLast
,deleteFront
,deleteLast
,getFront
,getRear
,isEmpty
,isFull
调用次数不大于2000
次
class MyCircularDeque {
static class Node {
Node prev;
int value;
Node next;
public Node(Node prev, int value, Node next) {
this.prev = prev;
this.value = value;
this.next = next;
}
}
int capacity;
int size = 0;
Node sentinel;
public MyCircularDeque(int k) {
this.capacity = k;
sentinel = new Node(null, 0, null); // Node中应该有一个默认值,这里使用0
sentinel.next = sentinel;
sentinel.prev = sentinel;
}
public boolean insertFront(int value) {
if (isFull()) {
return false;
}
Node added = new Node(sentinel, value, sentinel.next);
sentinel.next.prev = added;
sentinel.next = added;
size++;
return true;
}
public boolean insertLast(int value) {
if (isFull()) {
return false;
}
Node added = new Node(sentinel.prev, value, sentinel);
sentinel.prev.next = added;
sentinel.prev = added;
size++;
return true;
}
public boolean deleteFront() {
if (isEmpty()) {
return false; // 应该返回 false,而不是 null
}
Node front = sentinel.next;
sentinel.next = front.next;
front.next.prev = sentinel;
size--;
return true;
}
public boolean deleteLast() {
if (isEmpty()) {
return false; // 应该返回 false,而不是 null
}
Node rear = sentinel.prev;
sentinel.prev = rear.prev;
rear.prev.next = sentinel;
size--;
return true;
}
public int getFront() {
if (isEmpty()) {
return -1; // 返回-1表示队列为空
}
return sentinel.next.value;
}
public int getRear() {
if (isEmpty()) {
return -1; // 返回-1表示队列为空
}
return sentinel.prev.value;
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == capacity;
}
}
/**
* Your MyCircularDeque object will be instantiated and called as such:
* MyCircularDeque obj = new MyCircularDeque(k);
* boolean param_1 = obj.insertFront(value);
* boolean param_2 = obj.insertLast(value);
* boolean param_3 = obj.deleteFront();
* boolean param_4 = obj.deleteLast();
* int param_5 = obj.getFront();
* int param_6 = obj.getRear();
* boolean param_7 = obj.isEmpty();
* boolean param_8 = obj.isFull();
*/