简介

dequeue指的是双向队列,可以分别从队列的头部插入和获取数据,也可以从队列的尾部插入和获取数据。

本文将会介绍一下怎么创建dequeue和dequeue的一些基本操作。

双向队列的实现

和普通队列项目,双向队列可以分别在头部和尾部进行插入和删除工作,所以一个dequeue需要实现这4个方法:

  • insertFront(): 从dequeue头部插入数据
  • insertLast(): 从dequeue尾部插入数据
  • deleteFront(): 从dequeue头部删除数据
  • deleteLast(): 从dequeue尾部删除数据

同样的我们也需要一个head和一个rear来指向队列的头部和尾部节点。

也就是说实现了这四个方法的队列就是双向队列。我们不管它内部是怎么实现的。

接下来我们来直观的感受一下dequeue的插入和删除操作:

  1. 在头部插入

  1. 在尾部插入

  1. 在头部删除

  1. 在尾部删除

双向队列也可以有很多种实现方式,比如循环数组和链表。

双向队列的数组实现

因为数组本身已经有前后关系,也就是说知道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)。

本文的代码地址:

learn-algorithm

本文已收录于 http://www.flydean.com/13-algorithm-dequeue/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

看动画学算法之:双向队列dequeue的更多相关文章

  1. 看动画学算法之:队列queue

    目录 简介 队列的实现 队列的数组实现 队列的动态数组实现 队列的链表实现 队列的时间复杂度 简介 队列Queue是一个非常常见的数据结构,所谓队列就是先进先出的序列结构. 想象一下我们日常的排队买票 ...

  2. 看动画学算法之:排序-count排序

    目录 简介 count排序的例子 count排序的java实现 count排序的第二种方法 count排序的时间复杂度 简介 今天我们介绍一种不需要作比较就能排序的算法:count排序. count排 ...

  3. 看动画学算法之:linkedList

    目录 简介 linkedList的构建 linkedList的操作 头部插入 尾部插入 中间插入 删除节点 简介 linkedList应该是一种非常非常简单的数据结构了.节点一个一个的连接起来,就成了 ...

  4. 看动画学算法之:栈stack

    目录 简介 栈的构成 栈的实现 使用数组来实现栈 使用动态数组来实现栈 使用链表来实现 简介 栈应该是一种非常简单并且非常有用的数据结构了.栈的特点就是先进后出FILO或者后进先出LIFO. 实际上很 ...

  5. 看动画学算法之:平衡二叉搜索树AVL Tree

    目录 简介 AVL的特性 AVL的构建 AVL的搜索 AVL的插入 AVL的删除 简介 平衡二叉搜索树是一种特殊的二叉搜索树.为什么会有平衡二叉搜索树呢? 考虑一下二叉搜索树的特殊情况,如果一个二叉搜 ...

  6. 看动画学算法之:hashtable

    目录 简介 散列表的关键概念 数组和散列表 数组的问题 hash的问题 线性探测 二次探测 双倍散列 分离链接 rehash 简介 java中和hash相关并且常用的有两个类hashTable和has ...

  7. 看动画学算法之:二叉搜索树BST

    目录 简介 BST的基本性质 BST的构建 BST的搜索 BST的插入 BST的删除 简介 树是类似于链表的数据结构,和链表的线性结构不同的是,树是具有层次结构的非线性的数据结构. 树是由很多个节点组 ...

  8. 看动画学算法之:doublyLinkedList

    目录 简介 doublyLinkedList的构建 doublyLinkedList的操作 头部插入 尾部插入 插入给定的位置 删除指定位置的节点 简介 今天我们来学习一下复杂一点的LinkedLis ...

  9. STL之双向队列(dequeue)

    //双向队列 deque #include <deque> #include <cstdio> #include <algorithm> using namespa ...

随机推荐

  1. Docker部署启动错误,需要手动进入Docker的容器里,启动程序,排查错误

    #docker-compose build --no-cache //重新创建容器,不管有没有 #docker-compose up #docker-compose up -d //后台启动并运行容器 ...

  2. logstash-input-jdbc配置说明

    Logstash由三个组件构造成,分别是input.filter以及output.我们可以吧Logstash三个组件的工作流理解为:input收集数据,filter处理数据,output输出数据.至于 ...

  3. windows 下使用 mingw编译器 调试时 无法跟进源码

    windows 下使用 mingw编译器 调试时 无法跟进源码 最近在公司使用QT 开发,官方在线下载的 安装的QT mingw 都是没有debug版本的 由于没有debug版本动态库 所以你调试的时 ...

  4. mysql给数据库表里某个字段赋随机值

    UPDATE sxz_goods set sales_volume_base = round(rand() * 50) + 1 where sales_volume_base =0 ORDER BY ...

  5. Dapr实战(三)状态管理

    状态管理解决了什么 分布式应用程序中的状态可能很有挑战性. 例如: 应用程序可能需要不同类型的数据存储. 访问和更新数据可能需要不同的一致性级别. 多个用户可以同时更新数据,这需要解决冲突. 服务必须 ...

  6. Nginx系列(6)- nginx: [error] CreateFile() "D:\nginx-1.20.1/logs/nginx.pid" failed (2: The system cannot find the file specified)

    背景 修改nginx配置文件nginx.conf后,想要重启nginx使配置生效.cmd进入nginx安装目录,输入命令: nginx -s reload 报错:nginx: [error] Crea ...

  7. NOI.AC#2007-light【根号分治】

    正题 题目链接:http://noi.ac/problem/2007 题目大意 \(n\)个格子排成一排,每个格子有一个\(0/1\)和一个颜色.开始每个格子都是\(0\),\(q\)次操作取反一个颜 ...

  8. pandas 基础命令

    参考链接:https://github.com/rmpbastos/data_science/blob/master/_0014_Boost_your_Data_Analysis_with_Panda ...

  9. Dapr + .NET Core实战(九)本地调试

    前几节开发Dapr应用程序时,我们使用 dapr cli 来启动dapr服务,就像这样: dapr run --dapr-http-port 3501 --app-port 5001 --app-id ...

  10. 一款简单实用的串口通讯框架(SerialIo)

    前言 大龄程序员失业状态,前几天面试了一家与医疗设备为主的公司并录取:因该单位涉及串口通讯方面技术,自己曾做过通讯相关的一些项目,涉及Socket的较多,也使用SuperSocket做过一些项目,入职 ...