双端队列(Deque)原理及实现

简介: 双端队列支持在队头和队尾进行插入和删除操作,比标准队列更灵活。它可用链表或环形数组实现,头尾操作时间复杂度均为O(1)。适用于需频繁在两端操作的场景,如算法题中模拟栈或队列。

基本原理
如果你理解了前面讲解的内容,这个双端队列其实没啥可讲的了。所谓双端队列,主要是对比标准队列(FIFO 先进先出队列)多了一些操作罢了:
标准队列 只能在队尾插入元素,队头删除元素,而双端队列的队头和队尾都可以插入或删除元素。
普通队列就好比排队买票,先来的先买,后来的后买;而双端队列就好比一个过街天桥,两端都可以随意进出。当然,双端队列的元素就不再满足「先进先出」了,因为它比较灵活嘛。
在做算法题的场景中,双端队列用的不算很多。感觉只有 Python 用到的多一些,因为 Python 标准库没有提供内置的栈和队列,一般会用双端队列来模拟标准队列。
用链表实现双端队列
很简单吧,直接复用 我们之前实现的MyLinkedList 或者标准库提供的 LinkedList 就行了。因为双链表本就支持 O(1) 时间复杂度在链表的头尾增删元素:
Java
运行代码
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.LinkedList;

public class MyListDeque {
private LinkedList list = new LinkedList<>();

// 从队头插入元素,时间复杂度 O(1)
void addFirst(E e) {
    list.addFirst(e);
}

// 从队尾插入元素,时间复杂度 O(1)
void addLast(E e) {
    list.addLast(e);
}

// 从队头删除元素,时间复杂度 O(1)
E removeFirst() {
    return list.removeFirst();
}

// 从队尾删除元素,时间复杂度 O(1)
E removeLast() {
    return list.removeLast();
}

// 查看队头元素,时间复杂度 O(1)
E peekFirst() {
    return list.getFirst();
}

// 查看队尾元素,时间复杂度 O(1)
E peekLast() {
    return list.getLast();
}

public static void main(String[] args) {
    MyListDeque<Integer> deque = new MyListDeque<>();
    deque.addFirst(1);
    deque.addFirst(2);
    deque.addLast(3);
    deque.addLast(4);

    System.out.println(deque.removeFirst()); // 2
    System.out.println(deque.removeLast()); // 4
    System.out.println(deque.peekFirst()); // 1
    System.out.println(deque.peekLast()); // 3
}

}
用数组实现双端队列
也很简单吧,直接复用我们在 环形数组技巧 中实现的 CycleArray 提供的方法就行了。环形数组头尾增删元素的复杂度都是 O(1):
Java
运行代码
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class MyArrayDeque {
private CycleArray arr = new CycleArray<>();

// 从队头插入元素,时间复杂度 O(1)
void addFirst(E e) {
    arr.addFirst(e);
}

// 从队尾插入元素,时间复杂度 O(1)
void addLast(E e) {
    arr.addLast(e);
}

// 从队头删除元素,时间复杂度 O(1)
E removeFirst() {
    return arr.removeFirst();
}

// 从队尾删除元素,时间复杂度 O(1)
E removeLast() {
    return arr.removeLast();
}

// 查看队头元素,时间复杂度 O(1)
E peekFirst() {
    return arr.getFirst();
}

// 查看队尾元素,时间复杂度 O(1)
E peekLast() {
    return arr.getLast();
}

}

相关文章
|
4天前
|
Java API
用链表实现队列/栈
本文介绍如何用链表实现栈和队列,利用双链表头尾操作均为O(1)的特性,通过调用LinkedList API高效实现。栈可选头部或尾部作栈顶,队列同理,只需调整增删位置。文末引出数组实现队列的性能问题,启发优化思考。
|
4天前
|
存储 API 索引
队列/栈基本原理 ❗前置知识
本文介绍队列和栈两种“操作受限”的数据结构:队列遵循先进先出(FIFO),只能队尾入、队头出;栈遵循先进后出(FILO),仅在栈顶进行增删操作。二者底层多由数组或链表实现,核心API包括push、pop、peek和size,是后续复杂数据结构的基础。
|
4天前
|
Java 索引 容器
单/双链表代码实现
本文详解双链表与单链表的 MyLinkedList 实现,重点介绍三个关键优化:1)同时持有头尾节点引用,提升尾部操作效率;2)使用虚拟头尾节点简化边界处理;3)解析链表删除中的内存泄露误区,并强调指针置空的良好编程习惯。
|
3天前
|
存储 缓存 算法
学习数据结构和算法的框架思维
本文系统总结数据结构与算法本质:所有数据结构皆源于数组和链表,核心操作为遍历与访问;算法本质是穷举,关键在于无遗漏、无冗余。文章提炼出通用框架,帮助读者建立计算机思维,掌握高效解题方法,适合初学者建立全局观,也适合进阶者温故知新。
|
3天前
|
消息中间件 Kubernetes 网络协议
别老想着怎么用好 RPC 框架,你得多花时间琢磨原理
2011年加入京东,亲历技术演进,现任技术架构部首席架构师。主导微服务、消息中间件等核心系统研发,深耕分布式架构。课程涵盖RPC基础、进阶与高级实战,带你掌握网络通信核心,构建高效可靠分布式系统。(238字)
|
3天前
|
算法 Java 索引
双指针技巧秒杀七道数组题目
本文介绍双指针技巧在数组和链表中的应用,重点解析快慢指针如何实现原地修改。通过LeetCode经典题如删除有序数组/链表重复项,展示如何用慢指针记录结果、快指针遍历数据,高效完成去重,时间复杂度O(N),避免频繁数据搬移。
|
3天前
|
算法
双指针技巧秒杀七道链表题目
本文总结单链表七大技巧:合并有序链表、链表分解、合并K个有序链表、找倒数第k个节点、找中点、判断环及起点、判断相交及交点,均基于双指针思想,涵盖LeetCode多道经典题目,助你系统掌握链表算法核心。
|
3天前
|
JSON 前端开发 Java
另外几个接口文档
提供班级与学员信息管理功能,支持班级列表分页查询、添加、修改、删除及详情查看,同时支持学员信息条件查询,涵盖基本信息、班级关联、学历等字段,便于高效管理教学资源。
|
3天前
|
Java
多叉树的递归/层序遍历
多叉树是二叉树的扩展,节点可有多个子节点。遍历方式与二叉树类似,DFS无中序位置,BFS通过队列实现,支持按层遍历并记录深度,代码结构清晰,易于扩展。
|
3天前
|
算法
二叉树的递归/层序遍历
递归遍历(DFS)按固定顺序访问节点,前/中/后序取决于代码位置。层序遍历(BFS)借助队列实现,可逐层访问,常见写法能记录层数,适用于求深度、分层处理等场景。