一、队列介绍
- 队列是一个有序列表,可以用数组或链表来实现;
- 队列遵循FIFO原则(first in first out )
二、数组实现队列的思路分析
队列本身是有序列表,要生命一个队列,首先要定义:
- maxsize:队列最大容量;
- front:队列头部元素的下标(此处约定从0开始)即:队列的第一个元素就是arr[front];
- rear:队列尾部元素的下一个位置(此处约定rear从0开始);
- 队列满:(rear+1)% maxsize == front;
- 队列空:rear==front;
- 当前队列中元素个数:(rear-front+maxsize)%maxsize;
【tips】:此处牺牲一个空间用来判断队列是否满。
1、入队列
起始位置时,rear=0,front=0,当向队列添加元素时,先将当前要添加的值赋给arr[rear],然后将rear向后移动。直到添加了3个元素,rear指向滴个元素(rear=3),此时队r 我们认为队列已满,无法向队列添加元素了。
**【tips】**由于用数组实现队列,数组的容量是固定的,为了避免数组下标越界异常,向后移动 rear 时,不能直接加1,而应该通过取模运算,让rear的值在 1~maxsize 之间循环。因此向后移动rear时,rear =( rear+1)%maxsize.
2、出队列
左边队列已满,当从队列中取出一个数时,front向后移动一位(如右边队列)。此时 ( rear+1)%maxsize = 0, 而front=1,判断队列不是满的,可以继续添加数据。
【tips】:为了避免数组下标越界异常,向后移动 front 时,不能直接加1,而应该通过取模运算,让 front 的值在 0~maxsize-1 之间循环。因此向后移动 front 时,front =( front+1)%maxsize.
3、环形添加
如最左侧队列,此时可以继续添加元素,当向队列中添加元素时,rear向后移动形成中间队列图,当继续添加时(如右边图)又回到数组的第一个元素循环向队列中添加元素。
三、代码实现
package queue;
import java.util.Scanner;
/**
* 用数组实现循环队列
* @author
* @create 2020-07-13 9:33 AM
*/
public class CircleArrayQueue {
public static void main(String[] args) {
System.out.printf("开始测试");
//创建一个队列
CircleArray queue = new CircleArray(4);
char key = ' ';
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while(loop){
System.out.println("s (show):显示队列");
System.out.println("a (add):向队列添加元素");
System.out.println("g (get):从队列取出元素");
System.out.println("h (head):取出队列头部元素");
System.out.println("e (exit):退出程序");
key = scanner.next().charAt(0);//接收一个字符
switch (key) {
case 's':
queue.showQueue();
break;
case 'a':
System.out.println("请输入要添加的数据");
int value = scanner.nextInt();
queue.addQueue(value);
break;
case 'g':
try {
int getValue = queue.getQueue();
System.out.printf("取出的数据是:%d\n", getValue);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int headValue = queue.showQueueHead();
System.out.printf("当前队列头部的数据是:%d\n", headValue);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出~~~");
}
}
/**
* 分析:
* 队列空:rear==front
* 队列满:(rear+1)% maxsize == front
* 当前队列中数据个数:(rear-front+maxsize)% maxsiz
*/
class CircleArray{
// 队列的最大容量
private int maxsize;
// 存放队列数据的容器(用数组实现)。
private int[] arr;
// 队列中数据的头的位置:约定从0开始。
private int front;
//队列中数据的尾部元素的后一个位置,约定从0开始,此处会牺牲一个位置,用来判断队列是否满。
private int rear;
public CircleArray(int maxsize){
this.maxsize = maxsize;
arr = new int[maxsize];
//由于int类型默认值为0,因此不用特意为front 和 rear 赋值
}
/**
* 判断队列是否为空
*/
public boolean isEmpty(){
return rear==front;
}
/**
* 判断队列是否已满
* @return
*/
public boolean isFull(){
return (rear+1)%maxsize ==front;
}
/**
* 向队列添加数据,注意rear向后移动时,不能出现下标越界异常,需要取模运算让下标在1~maxsize之间循环
*/
public void addQueue(int n){
//1. 判断队列是否已满,如果满了就打印已满,无法添加数据
if(isFull()){
System.out.println("队列已满,无法添加数据");
return;
}
//2. 如果没满,就向队列中添加数据
//2.1由于rear指向队列尾部元素的后一个位置,所以直接给arr[rear]添加元素即可
arr[rear]=n;
//2.2添加完后,rear向后移动一位,但要注意数组越界,当队列已满,,
// 如果队列头部的位置有数据取出(有空位可继续存数据了),此时如果要存数,需要取模运算,再让下标回到数组头部。
// 如maxsize=3,rear的值一直是 0->1->2->3->1->2->3->1->2->3循环。
rear = (rear+1)%maxsize;
}
/**
* 从队列中取出元素(出队列)
* 注意front后移时,防止数组下标越界异常
*/
public int getQueue(){
//1.判断队列是否为空,如果为空则抛出异常(抛出异常后,自动return,不会执行后面的代码了,此处不用再return)。
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数~~~");
}
//2.如果不为空,正常取出数
//2.1 由于front指向队列头部第一个元素,因此取出arr[front]即可,先把要取的数存到临时变量
int tempVal = arr[front];
//2.2 front后移一位
front = (front+1)%maxsize;
//2.3 返回取出的数
return tempVal;
}
/**
* 显示队列的所有数据
* 分析:从队列头部第一个元素开始显示,有几个元素就显示几个元素
*/
public void showQueue(){
for(int i=front;i< front+queueSize();i++){
System.out.printf("arr[%d]= %d \n",i%maxsize,arr[i%maxsize]);
}
}
public int queueSize(){
return (rear-front+maxsize)%maxsize;
}
public int showQueueHead(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数~~~");
}
return arr[front];
}
}
当入队、出对的元素数量如图所示时,控制台打印结果: