队列
是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
队列是一种先进先出(First In First Out)的线性表,简称FIFO.允许插入的一段成为队尾,允许删除的一段为队头
数组的存储改进思路
-
思考:
在以往的数组中,N个元素,下标为0即为队头,入队时间复杂度O(1),但是取出时,意味着后续所有元素需要"平移",时间复杂度为O(n),现实中也是如此啊,一群人排队,第一个人走了,后面的人依次向前挪挪. -
改进1:
为什么出队列时一定要全部移动呢,如果不去限制队列的元素必须在存储在数组的前n个单元这一条街,出队性能就会大大增加,也就是说队头不一定在下标为0的位置
这样一来引入两个指针,front
指向队头元素,rear
指向队尾元素的下一个位置,这样front==rear
时就是空队列.
这样其实还有弊端,简单说单方向都依次递增,前面可能有空单元,但是两个指针挪到后面没地方了,
截图君上场,看后两个图或者后两段就行
循环队列
针对假溢出的现象,就再从头开始,也就是头尾相接的循环,我们把队列这种头尾项链的顺序存储结构称为循环队列
啊…还有一点比较繁琐,就是文中一个关键的点,也是就是怎么计算队列满的方法,有那么一点点废脑袋,当然我的代码中也用了自己的方法来判断.这个耐下心来细细琢磨也就那么回事
还是老样子,截图君上场,
前期的工作铺垫完毕,到了代码环节
代码实现
package com.company;
/**
* @Author: comfort
* @Date: 2020/8/3
*/
public class ArrQueueTest {
public static void main(String[] args) {
ArrQueueManager arrQueueManager = new ArrQueueManager();
String data = "31,21,17,16,77,99,88";
arrQueueManager.init(data.split(","));
arrQueueManager.out("初始化");
arrQueueManager.add(100);
arrQueueManager.add(120);
arrQueueManager.out("添加了俩个数以后");
arrQueueManager.add(130);
for (int i = 0; i < 5; i++) {
System.out.print ("取:"+arrQueueManager.get()+" ");
}
arrQueueManager.out("\n连续取了5个数后 ");
arrQueueManager.add(13);
arrQueueManager.add(42);
arrQueueManager.add(44);
arrQueueManager.add(45);
arrQueueManager.add(46);
arrQueueManager.add(48);
arrQueueManager.out("连续添加6个以后");
}
}
class ArrQueueManager {
ArrQueue arrQueue = new ArrQueue();
public int get() {
//这里判断不一样
if (arrQueue.front == arrQueue.rear) {
System.out.println("队列空了");
return -1;
}
int datum = arrQueue.data[arrQueue.front];
//恢复默认值,只是为了输出好辨别
arrQueue.data[arrQueue.front]=0;
//书本
// arrQueue.front=(arrQueue.front + 1)%arrQueue.MAXSIZE;
int front = arrQueue.front;
if (front + 1 == arrQueue.MAXSIZE) {
arrQueue.front=0;
}else{
arrQueue.front++;
}
return datum;
}
public void add(int value) {
//转了一圈
if ((arrQueue.rear + 1)%arrQueue.MAXSIZE == arrQueue.front) {
System.out.println("插入"+value+"错误:队列满了");
return;
}
arrQueue.data[arrQueue.rear] = value;
//书本
// arrQueue.rear=(arrQueue.rear + 1)%arrQueue.MAXSIZE;
//到达对尾
if (arrQueue.rear + 1 == arrQueue.MAXSIZE) {
arrQueue.rear = 0;
}else{
arrQueue.rear++;
}
}
public void init(String [] data) {
arrQueue.front = 0;
arrQueue.rear = 0;
if (data.length > arrQueue.MAXSIZE) {
System.out.println("错误:初始化太长了");
return;
}
for (int i = 0; i < data.length; i++) {
arrQueue.data[i]=Integer.parseInt(data[i]);
arrQueue.rear++;
}
}
public void out(String string) {
System.out.println(string+" 队列内容");
//输出要根据指针输出
//尾指针 还未到第二轮
if (arrQueue.front < arrQueue.rear) {
for (int i = arrQueue.front; i < arrQueue.rear; i++) {
System.out.print(arrQueue.data[i]+" ");
}
}else{
for (int i = arrQueue.front; i < arrQueue.MAXSIZE; i++) {
System.out.print(arrQueue.data[i]+" ");
}
for (int i = 0; i < arrQueue.rear; i++) {
System.out.print(arrQueue.data[i]+" ");
}
}
System.out.println("\n整个数组内容");
for (int datum : arrQueue.data) {
System.out.print(datum+" ");
}
System.out.println("======>front:"+arrQueue.front+" rear:"+arrQueue.rear);
}
}
class ArrQueue {
public int MAXSIZE = 10;
int[] data = new int[MAXSIZE];
/**
* 头指针
**/
int front;
/**
* 尾指针
**/
int rear;
}
中间输出废了一点功夫,总感觉不是那么回事,但是也凑合写出来了,希望以后我能自省找到更好的办法再来优化,也许有大佬提出来更好
老规矩 输出
初始化 队列内容
31 21 17 16 77 99 88
整个数组内容
31 21 17 16 77 99 88 0 0 0 ======>front:0 rear:7
添加了俩个数以后 队列内容
31 21 17 16 77 99 88 100 120
整个数组内容
31 21 17 16 77 99 88 100 120 0 ======>front:0 rear:9
插入130错误:队列满了
取:31 取:21 取:17 取:16 取:77
连续取了5个数后 队列内容
99 88 100 120
整个数组内容
0 0 0 0 0 99 88 100 120 0 ======>front:5 rear:9
插入48错误:队列满了
连续添加6个以后 队列内容
99 88 100 120 13 42 44 45 46
整个数组内容
42 44 45 46 0 99 88 100 120 13 ======>front:5 rear:4
主要内容是在插入130失败以后, 再进行删除,再进行插入
这个时候rear
就跑到前面去了.达到循环队列的意义
收获和不足
-
输出越来越熟练了,主要也懒得想其他的抽象方式
-
在判断
front
和rear
时没有看书独立思考,没有看书中的方式来判断,当然还是书里的方法香. -
在
get
时判断怎么为空时,偷了懒直接看书判断逻辑,在此特别记下,虽然看着书front==rear
看着简单,可是自己想想也要想一下 -
在输出时,还是改了两次,拿起来就敲的代码输出内容没有参考意义,还是指针顺着队列来一遍,这样才有对比.
拜了拜.下个文章见