看动画学算法之:双向队列dequeue
简介
dequeue指的是双向队列,可以分别从队列的头部插入和获取数据,也可以从队列的尾部插入和获取数据。
本文将会介绍一下怎么创建dequeue和dequeue的一些基本操作。
双向队列的实现
和普通队列项目,双向队列可以分别在头部和尾部进行插入和删除工作,所以一个dequeue需要实现这4个方法:
- insertFront(): 从dequeue头部插入数据
- insertLast(): 从dequeue尾部插入数据
- deleteFront(): 从dequeue头部删除数据
- deleteLast(): 从dequeue尾部删除数据
同样的我们也需要一个head和一个rear来指向队列的头部和尾部节点。

也就是说实现了这四个方法的队列就是双向队列。我们不管它内部是怎么实现的。
接下来我们来直观的感受一下dequeue的插入和删除操作:
- 在头部插入

- 在尾部插入

- 在头部删除

- 在尾部删除

双向队列也可以有很多种实现方式,比如循环数组和链表。
双向队列的数组实现
因为数组本身已经有前后关系,也就是说知道head可以拿到它后面一个数据,知道rear也可以拿到它前面一个数据。
所以数组的实现中,存储head和rear的index值已经够了。
我们只需要添加向头部插入数据和向尾部删除数据的方法即可:
//从头部入队列
public void insertFront(int data){
if(isFull()){
System.out.println("Queue is full");
}else{
//从头部插入ArrayDeque
head = (head + capacity - 1) % capacity;
array[head]= data;
//如果插入之前队列为空,将real指向head
if(rear == -1 ){
rear = head;
}
}
}
//从尾部取数据
public int deleteLast(){
int data;
if(isEmpty()){
System.out.println("Queue is empty");
return -1;
}else{
data= array[rear];
//如果只有一个元素,则重置head和real
if(head == rear){
head= -1;
rear = -1;
}else{
rear = (rear + capacity - 1)%capacity;
}
return data;
}
}
双向队列的动态数组实现
动态数组可以动态改变数组大小,这里我们使用倍增的方式来扩展数组。
看下扩展方法怎么实现:
//因为是循环数组,这里不能做简单的数组拷贝
private void extendQueue(){
int newCapacity= capacity*2;
int[] newArray= new int[newCapacity];
//先全部拷贝
System.arraycopy(array,0,newArray,0,array.length);
//如果rear<head,表示已经进行循环了,需要将0-head之间的数据置空,并将数据拷贝到新数组的相应位置
if(rear < head){
for(int i=0; i< head; i++){
//重置0-head的数据
newArray[i]= -1;
//拷贝到新的位置
newArray[i+capacity]=array[i];
}
//重置rear的位置
rear = rear +capacity;
//重置capacity和array
capacity=newCapacity;
array=newArray;
}
}
因为是循环数组,这里不能做简单的数组拷贝,我们需要判断rear和head的位置来判断是否进入到了循环结构。
如果进入到了循环结构,我们需要重置相应的字段数据,并拷贝到新数组中。
向头部插入数据和向尾部删除数据的方法和基本队列的实现是一致的,这里就不列出来了。
双向队列的链表实现
如果使用链表来实现双向队列会有什么问题呢?
在头部插入和在尾部插入都可以快速定位到目标节点。但是我们考虑一下尾部删除的问题。
尾部删除我们需要找到尾部节点的前一个节点,将这个节点置位rear节点。这就需要我们能够通过rear节点找到它的前一个节点。
所以基本的链表已经满足不了我们的需求了。 这里我们需要使用双向链表。
public class LinkedListDeQueue {
//head节点
private Node headNode;
//rear节点
private Node rearNode;
class Node {
int data;
Node next;
Node prev;
//Node的构造函数
Node(int d) {
data = d;
}
}
public boolean isEmpty(){
return headNode==null;
}
//从队尾插入
public void insertLast(int data){
Node newNode= new Node(data);
//将rearNode的next指向新插入的节点
if(rearNode !=null){
rearNode.next=newNode;
newNode.prev=rearNode;
}
rearNode=newNode;
if(headNode == null){
headNode=newNode;
}
}
//从队首插入
public void insertFront(int data){
if(headNode == null){
headNode= new Node(data);
}else{
Node newNode= new Node(data);
newNode.next= headNode;
headNode.prev= newNode;
headNode= newNode;
}
}
//从队首删除
public int deleteFront(){
int data;
if(isEmpty()){
System.out.println("Queue is empty");
return -1;
}else{
data=headNode.data;
headNode=headNode.next;
headNode.prev=null;
}
return data;
}
//从队尾删除
public int deleteLast(){
int data;
if(isEmpty()){
System.out.println("Queue is empty");
return -1;
}else{
data=rearNode.data;
rearNode=rearNode.prev;
rearNode.next=null;
}
return data;
}
}
双向链表中的每一个节点都有next和prev两个指针。通过这两个指针,我们可以快速定位到他们的后一个节点和前一个节点。
双向链表的时间复杂度
上面的3种实现的enQueue和deQueue方法,基本上都可以立马定位到要入队列或者出队列的位置,所以他们的时间复杂度是O(1)。
本文的代码地址:
本文已收录于 http://www.flydean.com/13-algorithm-dequeue/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
看动画学算法之:双向队列dequeue的更多相关文章
- 看动画学算法之:队列queue
目录 简介 队列的实现 队列的数组实现 队列的动态数组实现 队列的链表实现 队列的时间复杂度 简介 队列Queue是一个非常常见的数据结构,所谓队列就是先进先出的序列结构. 想象一下我们日常的排队买票 ...
- 看动画学算法之:排序-count排序
目录 简介 count排序的例子 count排序的java实现 count排序的第二种方法 count排序的时间复杂度 简介 今天我们介绍一种不需要作比较就能排序的算法:count排序. count排 ...
- 看动画学算法之:linkedList
目录 简介 linkedList的构建 linkedList的操作 头部插入 尾部插入 中间插入 删除节点 简介 linkedList应该是一种非常非常简单的数据结构了.节点一个一个的连接起来,就成了 ...
- 看动画学算法之:栈stack
目录 简介 栈的构成 栈的实现 使用数组来实现栈 使用动态数组来实现栈 使用链表来实现 简介 栈应该是一种非常简单并且非常有用的数据结构了.栈的特点就是先进后出FILO或者后进先出LIFO. 实际上很 ...
- 看动画学算法之:平衡二叉搜索树AVL Tree
目录 简介 AVL的特性 AVL的构建 AVL的搜索 AVL的插入 AVL的删除 简介 平衡二叉搜索树是一种特殊的二叉搜索树.为什么会有平衡二叉搜索树呢? 考虑一下二叉搜索树的特殊情况,如果一个二叉搜 ...
- 看动画学算法之:hashtable
目录 简介 散列表的关键概念 数组和散列表 数组的问题 hash的问题 线性探测 二次探测 双倍散列 分离链接 rehash 简介 java中和hash相关并且常用的有两个类hashTable和has ...
- 看动画学算法之:二叉搜索树BST
目录 简介 BST的基本性质 BST的构建 BST的搜索 BST的插入 BST的删除 简介 树是类似于链表的数据结构,和链表的线性结构不同的是,树是具有层次结构的非线性的数据结构. 树是由很多个节点组 ...
- 看动画学算法之:doublyLinkedList
目录 简介 doublyLinkedList的构建 doublyLinkedList的操作 头部插入 尾部插入 插入给定的位置 删除指定位置的节点 简介 今天我们来学习一下复杂一点的LinkedLis ...
- STL之双向队列(dequeue)
//双向队列 deque #include <deque> #include <cstdio> #include <algorithm> using namespa ...
随机推荐
- 学生信息管理系统.cpp(大二上)
#include<iostream> #include<fstream> #include<string> #include<iomanip> #i ...
- 【C++基础教程】第四课
上次的课后练习 第1题输出: 第二题输出:1 第三题: #include<iostream> #include<cmath> using namespace std; int ...
- OpenGL渲染管道,Shader,VAO&VBO&EBO
OpenGL渲染管线 (也就是)OpenGL渲染一帧图形的流程 以下列举最简单的,渲染一个三角形的流程,你可以将它视为 精简版OpenGL渲染管线 更复杂的流程也仅仅就是:在此基础上的各个流程中 添加 ...
- 2.docker安装及原理
一. docker的架构 1.1 docker的架构 先来看docker官网给出的docker架构图: 看官网,docker的架构描述: https://docs.docker.com/get-sta ...
- Java基础系列(28)- 方法的定义和调用
方法的定义 Java的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含以下语法: 修饰符 返回值类型 方法名(参数类型 参数名){ -- 方法体 -- retu ...
- 自己写登陆验证及借用auth的is_authenticated验证是否登陆
一. 自己写登陆需要注册,一个session的处理,还有一个session的删除 #自定义一个闭包装饰器,来验证def checkLogin(func): def wrapper(request, * ...
- 踩坑系列《十二》解决连接云服务器的redis失败
在本地连接服务器redis的时候,发现连接失败,这是因为服务器上的redis开启保护模式运行,该模式下是无法进行远程连接的.只需要修改redis目录下的redis.conf文件,找到 protecte ...
- springcloud整合config组件
config组件 config组件支持两种配置文件获取方式springcould搭建的微服务的配置文件的获取方式有两种.它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中或者本地 ...
- springboot事务的传播行为和隔离级别
springboot事务的传播行为和隔离级别 在springboot中事务的传播行为和隔离级别都是在TransactionDefinition这个接口中定义的 传播行为定义了7种,分别用0-6来表示 ...
- Python技法3:匿名函数、回调函数和高阶函数
1.定义匿名或内联函数 如果我们想提供一个短小的回调函数供sort()这样的函数用,但不想用def这样的语句编写一个单行的函数,我们可以借助lambda表达式来编写"内联"式的函数 ...