这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
本系列文章为个人学习总结,如果有发现错误或存在疑问之处,欢迎留言指点!
本文是重学数据结构系列的第三篇,系列文章如下:
1.算法时间复杂度和空间复杂度
2.重学数据结构--链表
3.重学数据结构--队列
4.重学数据结构--栈
5.重学数据结构--树
1.队列介绍
什么是队列?简单的理解,就是银行排队取钱,排在前面的先取钱,排在后面的后取钱。
- 队列是一个有序列表,可以用数组或链表来实现(我们这里用数组实现)
- 队列的特点:
先进先出,先存入队列的数据,要先取出。后存入的要后取出
在数据结构中我们常说的队列有单向队列和循环队列。下面我们用数组来模拟这两种队列。
2.单向队列
数组模拟思路:
-
队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中maxSize是该队列的最大容量。
-
因为队列的输出、输入是分别从前后端来处理,因此需要两个变量
front(头指针)及rear(尾指针)分别记录队列前后端的下标,front 会随着数据取出而改变,而rear则是随着数据取出而改变,如图所示: -
初始化
初始化数组 头指针下标==尾指针下标==-1(即队列没有任何元素) -
往队列添加数据
1)判断队列是否满,如果满了则不能添加: 尾指针下标=最大容量-1 2)尾指针往后移一个位置:rear+1; arr[rear] = n -
从队列取出数据
1)判断队列是否为空:头指针下标=尾指针下标 2)front往后移动一个位置:front+1
实现
public class ArrayQueue {
private int maxSize; //数组的最大容量
private int front; // 队列头
private int rear; // 队列尾
private int [] arr; // 用于存数据模拟队列
/**
* 初始化队列
* @param maxSize
*/
public ArrayQueue(int maxSize){
this.maxSize = maxSize;
arr = new int[maxSize];
rear = -1;
front = -1;
}
/**
* 判断队列是否为空
* @return
*/
public boolean isEmpty(){
return this.front == this.rear;
}
/**
* 判断队列是否满
* @return
*/
public boolean isFull(){
return this.rear == this.maxSize-1;
}
/**
* 入队
* @param value
*/
public void addQueue(int value){
//判断队列是否满
if (isFull()){
System.out.println("队列满,不能添加数据");
return;
}
this.arr[++rear] = value;
}
/**
* 出队
*/
public int getQueue(){
//判断是否为空
if(isEmpty()){
throw new RuntimeException("队列为空,没有数据可获取");
}
return arr[++front];
}
/**
* 显示队列所有元素
*/
public void showQueue(){
if(isEmpty()){
System.out.println("队列中没有元素");
return;
}
for (int i = front+1; i <= rear; i++) {
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
/**
* 显示队列头元素
*/
public int showQueueHead(){
//判断队列是否为空
if(isEmpty()){
throw new RuntimeException("队列为空,没有数据可获取");
}
return arr[front+1];
}
}
测试:
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(4);
queue.addQueue(1);
queue.addQueue(3);
queue.addQueue(4);
queue.addQueue(5);
queue.addQueue(6);
System.out.println("显示队列中所有元素:");
queue.showQueue();
System.out.println("队列头部元素为"+queue.showQueueHead());
System.out.println();
System.out.println("出队元素为"+queue.getQueue());
System.out.println("出队元素为"+queue.getQueue());
System.out.println("出队元素为"+queue.getQueue());
}
3.循环队列
对于普通队列而言,数组不能重复使用,这样就造成了空间浪费。怎样才能使队列能够重复利用呢?将队列改成一个环形的,这样就可以重复利用了。
思路:
- front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素front 的初始值 = 0
- rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.rear 的初始值 = 0
- 当队列满时,条件是
(rear + 1) % maxSize == front - 对队列为空的条件,
rear == front - 当我们这样分析, 队列中有效的数据的个数
(rear + maxSize - front) % maxSize - 我们就可以在单向的队列上修改得到,一个环形队列
实现:
public class CircleQueue {
private int maxSize; //数组的最大容量
private int rear; //队列尾 rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.
private int front; //队列头
private int[] arr; //用于存放数据,模拟队列
/**
* 初始化队列
* @param maxSize
*/
public CircleQueue(int maxSize){
this.maxSize = maxSize;
arr = new int[maxSize];
}
/**
* 判断队列是否满
* @return
*/
public boolean isFull(){
System.out.println("rear="+rear+"\tfront="+front);
return (rear+1)%maxSize==front;
}
/**
* 判断队列是否为空
* @return
*/
public boolean isEmpty(){
return rear == front;
}
/**
* 往队列添加数据
* @param n
*/
public void addQueue(int n){
//判断是否满
if (isFull()){
System.out.println("队列满,不能添加数据");
return;
}
arr[rear] = n;
rear = (rear+1)%maxSize;
}
/**
* 取出数据
* @return
*/
public int getQueue(){
//判断是否为空
if(isEmpty()){
throw new RuntimeException("队列为空,没有数据可取");
}
int value = arr[front];
front = (front+1)%maxSize;
System.out.println("rear="+rear+"\tfront="+front);
return value;
}
/**
* 显示队列所有数据
*/
public void showQueue(){
//判断是否为空
if(isEmpty()){
System.out.println("队列为空,没有数据可显示");
return;
}
for (int i = front; i < front + size(); i++) {
System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
}
}
/**
* 显示队头数据
* @return
*/
public int showQueueHead(){
//判断是否为空
if(isEmpty()){
throw new RuntimeException("队列为空,没有数据可显示");
}
return arr[front];
}
/**
* 求当前队列有效数个数
*/
public int size(){
return (rear-front+maxSize)%maxSize;
}
}
测试:
public static void main(String[] args) {
CircleQueue queue = new CircleQueue(4);
boolean loop = true;
char key = ' '; //接受用户输入
Scanner sc = new Scanner(System.in);
while(loop){
System.out.println("s(show)显示队列");
System.out.println("g(get)从队列取出数据");
System.out.println("a(add)添加数据");
System.out.println("h(Head)显示队列头数据");
System.out.println("e(exit)");
key = sc.next().charAt(0); //接收一个字符串
switch (key){
case 's':
queue.showQueue();
break;
case 'g':
try {
int value = queue.getQueue();
System.out.printf("取出的数据为%d\n",value);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'a':
System.out.println("输入你要添加的数");
int val = sc.nextInt();
queue.addQueue(val);
break;
case 'h':
try {
int value = queue.showQueueHead();
System.out.printf("队列头部的数据为%d\n",value);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
sc.close();
loop = false;
break;
default:
break;
}
}
}
\