双端队列(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();
}

}

目录
相关文章
|
C++
【PTA】​L1-006 连续因子​(C++)
【PTA】​L1-006 连续因子​(C++)
733 0
【PTA】​L1-006 连续因子​(C++)
|
存储 缓存 算法
Python中collections模块的deque双端队列:深入解析与应用
在Python的`collections`模块中,`deque`(双端队列)是一个线程安全、快速添加和删除元素的双端队列数据类型。它支持从队列的两端添加和弹出元素,提供了比列表更高的效率,特别是在处理大型数据集时。本文将详细解析`deque`的原理、使用方法以及它在各种场景中的应用。
1220 4
|
4月前
|
存储 NoSQL 算法
Docker安装Redis
本文介绍Docker安装Redis单机与集群部署,涵盖配置文件映射、数据卷挂载及3主3从集群搭建。深入解析Redis集群采用的哈希槽分区机制,对比哈希取余与一致性哈希算法,阐述其扩容缩容、数据分布与节点管理原理,助力构建高可用分布式缓存体系。(238字)
96 0
|
15天前
|
人工智能 Java 调度
Java21 虚拟线程实践:框架高并发升级之路
本框架完成面向AI场景的技术升级:全栈适配JDK21,深度集成虚拟线程与结构化并发;重构线程模型,显著提升IO密集型任务(如大模型调用、向量检索)的并发能力与CPU利用率,夯实高并发、低开销的企业级AI服务底座。(239字)
98 5
|
4月前
|
Java Maven 数据安全/隐私保护
Nexus仓库
本文介绍Linux环境下Nexus Repository Manager OSS的安装与配置,包括JDK8环境搭建、Nexus下载解压、服务启动及Web访问。涵盖登录密码管理、仓库创建、Docker部署、数据持久化、Maven/NPM/Docker私仓配置与资源上传等核心操作,助力搭建高效私有仓库。
286 0
|
4月前
|
缓存 Ubuntu Linux
Docker安装
本文介绍CentOS系统下安装、配置及卸载Docker的完整步骤,涵盖卸载旧版本、配置阿里云镜像源、安装Docker引擎、启动服务、运行HelloWorld测试,并提供离线安装与系统服务配置方法,同时包含daemon.json参数设置、日志管理、命令补全等高级配置,助力快速部署Docker环境。
248 0
|
4月前
|
Java Maven 数据安全/隐私保护
nexus私仓环境搭建
本文介绍Nexus Repository Manager OSS的安装与配置,包括JDK环境准备、Nexus下载解压、用户创建及服务启动。详细说明如何配置Maven、Docker私仓,实现jar包上传与镜像推送,并支持匿名访问。同时涵盖npm、helm等仓库的搭建要点,适用于企业级私有化部署需求。(239字)
367 0
|
4月前
|
监控 安全 机器人
钉钉通知
本文介绍如何通过Java代码调用钉钉机器人API实现系统告警消息实时推送。涵盖机器人创建、Webhook配置、Postman测试及Java代码封装,强调关键词匹配与限流规则,助力开发人员高效集成钉钉通知,提升系统监控响应能力。(238字)
598 0
|
4月前
|
Ubuntu Shell Linux
Docker常用命令
本文介绍了Docker常用命令,涵盖启动、停止、重启、状态查看及开机自启等基础操作,版本与帮助信息查询,镜像的列出、搜索、下载、删除及空间管理,虚悬镜像处理,命令自动补全配置方法,以及后台运行Linux容器和yum下载依赖技巧,适用于Docker日常运维与开发。
145 0
|
10月前
|
存储 JavaScript 前端开发
Set中的add()方法和数组的push()方法有什么区别?
Set中的add()方法和数组的push()方法有什么区别?
510 122