使用java语言实现一个队列(两种实现比较)(数据结构)
一.什么是队列,换句话说,队列主要特征是什么?
四个字:先进先出
六个字:屁股进,脑袋出
脑补个场景:日常排队买饭,新来的排在后面,前面打完饭的走人,这就是队列;
OK,思考一个问题,我为什么写了两种实现,它们的区别是什么,哪个性能更好一些?
我觉得学习一定要带着问题来学习;
二.队列的两种实现
1.数组队列
数组队列比较简单,基于之前写的动态数组所实现的,基本方法都是根据队列的特性从而选择性的调用动态数组的方法来实现的。
public class ArrayQueue<E> implements Queue<E> {
private Array<E> array; public ArrayQueue (int catacity) {
array = new Array<E>(catacity);
} public ArrayQueue () {
array = new Array<E>();
}
//查看队列容量
public int catacity () {
return array.getCapacity();
}
//获取队列中元素个数
@Override
public int getSize() {
return array.getSize();
}
//判断队列是否为空
@Override
public boolean isEmpty() {
return array.isEmpty();
}
//入队 时间复杂度:O(1)
@Override
public void enqueue(E e) {
array.addLast(e);
}
//出队 时间复杂度:O(n)
@Override
public E dequeue() {
return array.removeFirst();
}
//查看队首元素 时间复杂度: O(1)
@Override
public E getFront() {
return array.getFirst();
}
@Override
public String toString () {
StringBuilder res = new StringBuilder();
res.append("Queue");
res.append("front [");
for (int i = 0; i < getSize(); i++) {
res.append(array.getByIndex(i));
if (i != array.getSize() - 1) {
res.append(",");
}
}
res.append("] tail");
return res.toString();
} public static void main (String[] args) {
ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
for(int i = 0; i < 10; i++) {
arrayQueue.enqueue(i);
System.out.println(arrayQueue);
if (i % 3 == 2) {
System.out.println(arrayQueue);
}
}
}
}
2.循环队列
什么是循环队列?用图来说明
(1)队首在0位置,队尾在4位置,队列里面有4个元素
(2)此时,我进行两次出队操作,队首位置为2,队首前面空了两个空间。
(3)我再进行三次入队操作,其中一个元素插到了数组的前面。
循环队列:就是当数组末端没有位置时,新的入队元素则插到数组前面空余的空间中,避免了空间的浪费,可以把整个数组想象成一个环状。
代码实现:
public class LoopQueue<E> implements Queue<E> { private E[] data;
//队首
private int front;
//队尾
private int tail;
//队列中元素个数
private int size; public LoopQueue (int catacity) {
//实际开辟的容量比用户定义的容量大一位
data = (E[])new Object[catacity + 1];
front = 0;
tail = 0;
size = 0;
} public LoopQueue () {
this(10);
}
//获取队列容量
public int getCapacity () {
return data.length - 1;
}
//获取队列元素个数
@Override
public int getSize() {
return size;
}
当front == tail 时,定义队列为空
//队列是否为空
@Override
public boolean isEmpty() {
return tail == front;
}
//入队
@Override
public void enqueue(E e) {
//判断数组空间满没满,满了的话进行扩容
if ((tail + 1) % data.length == front) {
resize(getCapacity() * 2);
} data[tail] = e;
tail = (tail + 1) % data.length;
size ++;
}
//出队
@Override
public E dequeue() {
if (isEmpty()) {
throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
}
E set = data[front];
data[front] = null;
front = (front + 1) % data.length;
size --;
//缩容
if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
resize(getCapacity()/2);
}
return set;
}
//获取队首元素
@Override
public E getFront() {
if (isEmpty()) {
throw new IllegalArgumentException("Queue is empty.");
}
return data[front];
} //扩容
public void resize (int catacity) {
E[] newArray = (E[])new Object[catacity];
//将旧数组中元素复制到新数组中
for (int i = 0; i < size; i++) {
newArray[i] = data[(i + front) % data.length];
}
data = newArray;
front = 0;
tail = size;
} @Override
public String toString () {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("LoopQueue");
stringBuilder.append("front [");
for (int i = 0; i < size; i++) {
stringBuilder.append(data[(i + front) % data.length]);
if (i != size - 1) {
stringBuilder.append(",");
}
}
stringBuilder.append("] tail");
return stringBuilder.toString();
}
循环队列总结:
三.两种队列的简单比较
分别用数组队列和循环队列,将10万个数,入队再出队;
public static Double testQueue (Queue<Integer> queue, int opCount) { long startTime = System.nanoTime();
Random random = new Random(); for (int i = 0; i < opCount; i++) {
queue.enqueue(random.nextInt(Integer.MAX_VALUE));
}
for (int i = 0; i < opCount; i++) {
queue.dequeue();
} long endTime = System.nanoTime();
return ( endTime - startTime) / 1000000000.0;
} public static void main(String[] args) {
int opCount = 100000;
Queue<Integer> loopQueue = new LoopQueue<>();
Queue<Integer> arrayQueue = new ArrayQueue<>();
System.out.println("ArrayQueue, time1 = " + Main.testQueue(arrayQueue,opCount));
System.out.println("LoopQueue, time2 = " + Main.testQueue(loopQueue,opCount)); //new Main().stack();
}
测试结果:两者差距近200倍。
最后,
浮于表面看千遍,
不如自己思一遍,
希望这篇文章能够对你起到帮助。
使用java语言实现一个队列(两种实现比较)(数据结构)的更多相关文章
- JAVA 集合 List 分组的两种方法
CSDN日报20170219--<程序员的沟通之痛> [技术直播]揭开人工智能神秘的面纱 程序员1月书讯 云端应用征文大赛,秀绝招,赢无人机! JAVA 集合 List 分组的两种方法 2 ...
- 使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网)
使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网) 一,前期准备 1,Java IDE(Eclipse)与JDK的安装与配置jdk-15.0.1-免配置路径版提取码:earu免安装版 ...
- java中数组复制的两种方式
在java中数组复制有两种方式: 一:System.arraycopy(原数组,开始copy的下标,存放copy内容的数组,开始存放的下标,需要copy的长度); 这个方法需要先创建一个空的存放cop ...
- Java中Compareable和Comparator两种比较器的区别
Java中Compareable和Comparator两种比较器的区别 参考原文链接:https://www.cnblogs.com/ldy-blogs/p/8488138.html 1.引言 在ja ...
- Java中HashMap遍历的两种方式
Java中HashMap遍历的两种方式 转]Java中HashMap遍历的两种方式原文地址: http://www.javaweb.cc/language/java/032291.shtml 第一种: ...
- java动态获取WebService的两种方式(复杂参数类型)
java动态获取WebService的两种方式(复杂参数类型) 第一种: @Override public OrderSearchListRes searchOrderList(Order_Fligh ...
- 一个label两种颜色,一个label两种字体
-(void)addLabel{ UILabel *label = [[UILabel alloc]init]; label.backgroundColor = [UIColor grayColor] ...
- Java使用SFTP和FTP两种连接方式实现对服务器的上传下载 【我改】
[]如何区分是需要使用SFTP还是FTP? []我觉得: 1.看是否已知私钥. SFTP 和 FTP 最主要的区别就是 SFTP 有私钥,也就是在创建连接对象时,SFTP 除了用户名和密码外还需要知道 ...
- java字符串大小写转换的两种方法
转载自:飞扬青春sina blogjava字符串大小写转换的两种方法 import java.io..* public class convertToPrintString { pu ...
随机推荐
- 用了 10 多年的 Tomcat 居然有bug !
Java技术栈 www.javastack.cn 优秀的Java技术公众号 为了解决分布式链路追踪的问题,我们引入了实现OpenTracing的Jaeger来实现.然后我们为SpringBoot框架写 ...
- 51nod 1963 树上Nim
这题还真就是树上玩 Nim... 相关知识点就是阶梯博弈,具体可以康这里 →_→ PS:手动搜索阶梯博弈 然后这题就转化为了多路径的阶梯博弈,这样的话咱定义根节点深度为 0,然后把所有奇数深度点的权值 ...
- 如何将数据库导入到本地MySQL
有两个方法:(1)在MySQL的客户端进行导入,比如: http://jingyan.baidu.com/article/6dad507517c11aa123e36ea0.html (2)方法:常用s ...
- [零基础学python]啰嗦的除法
除法啰嗦的,不仅是python. 整数除以整数 看官请在启动idle之后.练习以下的运算: >>> 2/5 0 >>> 2.0/5 0.4 >>> ...
- Css网页样式设计
第一章 概述 一.CSS简介1.CSS是Cascading Style Sheets(层叠样式表单)的简称.通常所称的CSS是指CSS1,即层叠样式表单1级. 2.编辑CSS文档:与编辑HTML的方法 ...
- 原生js实现深度克隆
总体思路: 判断对象当中的值为引用值还是原始值 如果是引用值,判断是数组还是对象,如果是原始值直接copy 递归 注意:不要忘了排除null,因为typeof null = 'object' func ...
- python利用(threading,ThreadPoolExecutor.map,ThreadPoolExecutor.submit) 三种多线程方式处理 list数据
需求:在从银行数据库中取出 几十万数据时,需要对 每行数据进行相关操作,通过pandas的dataframe发现数据处理过慢,于是 对数据进行 分段后 通过 线程进行处理: 如下给出 测试版代码,通过 ...
- centos7安装mxnet
pip install mxnet-cu90 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com 安装sklearn时总报错 ...
- 给虚拟机CentOS7扩容(lvm方式)
虚拟机中centos7原有容量不够了,需要进行扩容. 可以使用图形工具gparted来进行操作,安装和使用可自行百度.但需要注意的是,这篇文章提到:一定要用parted中的mkfs命令格式化分区,用系 ...
- Proxy + Reflect 实现 响应的数据变化
Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等) let p = new Proxy(target, handler); get(target, propKey, r ...