一文详解「队列」,手撸队列的3种方法!
本文已收录至我的 Github《算法图解》系列:https://github.com/vipstone/algorithm
前面我们介绍了栈(Stack),队列和栈是比较像的一种数据结构。我们可以想象有很多辆汽车正在通过单行道的隧道,所有车辆不能插队、不能掉头,先进来的车也先出去,我们可以把这种特征的数据结构称之为队列。
队列也属于逻辑结构,所谓的物理结构是指可以将数据存储在物理空间中,比如数组和链表都属于物理数据结构;而逻辑结构则是用于描述数据间的逻辑关系的,它可以由多种不同的物理结构来实现,比如队列和栈都属于逻辑结构。
队列特性
队列中的元素必须是先进先出(First In First Out,FIFO)的,它有两个重要的方法:入队(enqueue)和出队(dequeue)。队列的入口端叫队尾(rear),出口端叫队头(front),如下图所示:
手撸队列
学习了队列的基本知识之后,接下来我们将使用代码来实现一个队列。
首先我们先使用数组来实现一个队列,它的结构如下图所示:
1.自定义队列—数组
public class MyQueue<E> {
private Object[] queue; // 存储容器
private int head; // 头部指针
private int tail; // 尾部指针
private int size; // 队列实际存储长度
private int maxSize; // 最大容量
public MyQueue() {
// 无参构造函数,设置默认参数
this.maxSize = 10;
this.head = 0;
this.tail = -1;
this.size = 0;
this.queue = new Object[this.maxSize];
}
public MyQueue(int initSize) {
// 有参构造函数,设置参数
this.maxSize = initSize;
this.head = 0;
this.tail = -1;
this.size = 0;
this.queue = new Object[this.maxSize];
}
/**
* 查询队头元素
*/
public E peek() throws Exception {
if (size == 0) {
throw new Exception("队列中暂无数据");
}
return (E) this.queue[this.head];
}
/**
* 入列
*/
public boolean offer(E e) throws Exception {
if (tail >= (maxSize - 1)) {
throw new Exception("添加失败,队列已满");
}
this.queue[++tail] = e;
size++;
return true;
}
/**
* 出列
*/
public E poll() throws Exception {
if (size == 0) {
throw new Exception("删除失败,队列为空");
}
size--;
return (E) this.queue[head++];
}
/**
* 代码测试
*/
public static void main(String[] args) throws Exception {
MyQueue queue = new MyQueue();
queue.offer("Hello");
queue.offer("Java");
System.out.println(queue.peek());
queue.poll();
System.out.println(queue.poll());
}
}
以上代码的执行结果如下:
Hello Java
2.自定义队列—链表
用链表实现队列的数据结构如下图所示:
实现代码如下:
public class QueueByLinked {
/**
* 声明链表节点
*/
static class Node<E> {
E item; // 当前的值
Node<E> next; // 下一个节点
Node(E e) {
this.item = e;
}
}
private Node firstNode; // 队头元素
private Node lastNode; // 队尾元素
private int size; // 队列实际存储数量
private int maxSize; // 队列最大容量
public QueueByLinked(int maxSize) {
if (maxSize <= 0) throw new RuntimeException("队列最大容量不能为空");
// 默认初始化函数
firstNode = lastNode = new Node(null);
this.size = 0;
this.maxSize = maxSize;
}
/**
* 判断队列是否为空
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 入列
*/
public void offer(Object e) {
// 最大值效验
if (maxSize <= size) throw new RuntimeException("队列已满");
Node node = new Node(e);
lastNode = lastNode.next = node; // 设置最后一个节点和倒数第二个节点的 next
size++; // 队列数量 +1
}
/**
* 出列
*/
public Node poll() {
if (isEmpty()) throw new RuntimeException("队列为空");
size--; // 队列数量 -1
return firstNode = firstNode.next; // 设置并返回队头元素(第一个节点是 null,当前元素则为 Node.next)
}
/**
* 查询队头元素
*/
public Node peek() {
if (isEmpty()) throw new RuntimeException("队列为空");
return firstNode.next; // 返回队头元素(第一个节点是 null,当前元素则为 Node.next)
}
/**
* 代码测试
*/
public static void main(String[] args) {
QueueByLinked queue = new QueueByLinked(10);
queue.offer("Hello");
queue.offer("JDK");
queue.offer("Java");
System.out.println(queue.poll().item);
System.out.println(queue.poll().item);
System.out.println(queue.poll().item);
}
}
以上代码的执行结果如下:
Hello JDK Java
3.扩展:使用 List 实现自定义队列
除了以上两种方式之外,我们还可以使用 Java 自身的数据结构来实现队列,比如 List,我们这里提供一个实现的思路(但并不建议在实际工作中使用),实现代码如下:
import java.util.ArrayList;
import java.util.List;
/**
* 自定义队列(List方式)
*/
public class QueueByList<E> {
private List value; // 队列存储容器
public QueueByList() {
// 初始化
value = new ArrayList();
}
/**
* 判断队列是否为空
*/
public boolean isEmpty() {
return value.size() == 0;
}
/**
* 入列
*/
public void offer(Object e) {
value.add(e);
}
/**
* 出列
*/
public E poll() {
if (isEmpty()) throw new RuntimeException("队列为空");
E item = (E) value.get(0);
value.remove(0);
return item;
}
/**
* 查询队头元素
*/
public E peek() {
if (isEmpty()) throw new RuntimeException("队列为空");
return (E) value.get(0);
}
/**
* 代码测试
*/
public static void main(String[] args) {
QueueByList queue = new QueueByList();
queue.offer("Hello");
queue.offer("JDK");
queue.offer("Java");
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
}
}
以上代码的执行结果如下:
Hello JDK Java
队列使用场景
队列的常见使用场景有:
- 存储多线程中等待排队执行的任务;
- 存储多线程公平锁中等待执行任务的线程;
- 常见消息中间件的任务队列等。
总结
通过以上三种队列的实现方式我们可以看出,任意容器都是可以用来实现队列(Queue)的,只要保证队列的元素先进先出(FIFO),并且在实现类中需要包含队列的四个核心方法:入列、出列、查询队列是否为空、返回队头元素等,就可以称为实现了一个自定义的队列。
最后,给大家留一个问题:队列的类型都有哪些?欢迎评论区留言,我会在下篇文章中给出答案。欢迎关注我,每天和你一起进步一点点~
- 通过添加HTTP Header实现上下文数据在WCF的自动传递
- 模拟在WCF中的应用
- [WCF权限控制]通过扩展自行实现服务授权[提供源码下载]
- [WCF权限控制]WCF自定义授权体系详解[原理篇]
- [WCF权限控制]WCF自定义授权体系详解[原理篇]
- [WCF权限控制]WCF自定义授权体系详解[实例篇]
- [ASP.NET MVC]通过对HtmlHelper扩展简化“列表控件”的绑定
- [ASP.NET MVC]为HtmlHelper添加一个RadioButtonList扩展方法
- 在ASP.NET MVC中使用“RadioButtonList”和“CheckBoxList”
- [WCF REST] 一个简单的REST服务实例
- [WCF REST] 提高性能的一个有效的手段:条件资源获取(Conditional Retrieval)
- [WCF REST] 解决资源并发修改的一个有效的手段:条件更新(Conditional Update)
- [WCF REST] 通过ASP.NET Output Caching实现声明式缓存
- [WCF REST] 通过ASP.NET Output Caching实现声明式缓存
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python项目跨域问题解决方案
- keras的load_model实现加载含有参数的自定义模型
- opencv 图像加法与图像融合的实现代码
- PHP图像处理技术实例总结【绘图、水印、验证码、图像压缩】
- python如何实现读取并显示图片(不需要图形界面)
- PHP文件操作实例总结【文件上传、下载、分页】
- Django中Aggregation聚合的基本使用方法
- ThinkPHP3.2.3框架实现执行原生SQL语句的方法示例
- PHP反射实际应用示例
- 解决Keras使用GPU资源耗尽的问题
- 如何表示python中的相对路径
- 基于Tensorflow读取MNIST数据集时网络超时的解决方式
- python中的错误如何查看
- python实现斗地主分牌洗牌
- Python Mock模块原理及使用方法详解