队列、环形队列
队列的一个使用场景:
银行排队案例------>先进先出
队列介绍:
队列是一个有序列表,可以用数组和链表实现
数组模拟队列:
数组的maxSize是该队列的最大容量
因为队列的输入和输出是分别从前后端来处理,因此需要两个变量front(头)和rear(尾)分别记录队列前后端的下标,头会随着输出改变,尾会随着输入改变
存队列---->addQueue(); 1.若尾指针往后移:rear+1,当front == rear [空] 2.将尾指针rear小于队列的最大下标maxSize-1,则将数据存入rear所指向的数据元素里,否则无法存入数据。rear == maxSize-1 [队列满]
//代码实现
import java.util.Scanner;
//数组模拟队列
public class ArrayToQueue {
public static void main(String[] args) {
//测试
//创建队列
ArrayQueue aq = new ArrayQueue(3);
char key = ' ';//接收用户输入
Scanner sc = new Scanner(System.in);
boolean loop = true;
//输出一个菜单
while(loop) {
System.out.println("请输入你需要进行的队列操作:");
System.out.println("a(add):添加数据到队列");
System.out.println("g(get):从队列中获取数据");
System.out.println("s(show):显示队列所有数据");
System.out.println("h(head):显示头数据");
System.out.println("e(exit):退出程序");
key = sc.next().charAt(0);//接收一个字符
switch (key) {
case 'a':
System.out.println("请输入一个需要添加的数据:");
int value = sc.nextInt();
aq.addQueue(value);
break;
case 'g':
try {
int res = aq.getQueue();
System.out.printf("获取的数据是:%d\n",res);
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case 's':
aq.showQueue();
break;
case 'h':
try {
int res = aq.headQueue();
System.out.printf("队列头数据是:%d\n",res);
}catch(Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
sc.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
//数组模拟队列
class ArrayQueue{
private int maxSize;//表示数组的最大容量
private int front;//队列头
private int rear;//队列尾
private int[] arr;//用于存放数据,模拟队列
//创建队列的构造器
public ArrayQueue(int arrMaxSize) {
super();
maxSize = arrMaxSize;
arr = new int[maxSize];
front = -1;//指向队列头部,指向队列头的前一个位置
rear = -1;//指向队列尾部,指向队列尾的具体数据
}
//判断队列是否满
public boolean isFull() {
return rear == maxSize-1;
}
//判断队列是否空
public boolean isEmpty() {
return rear == front;
}
//添加数据到队列,加到尾部
public void addQueue(int n) {//传入数据为n
//1.判断队列是否满
if(isFull()) {
System.out.println("队列满,不能加入数据");
return;
}else {
rear++;//尾部后移
arr[rear] = n;
}
}
//获取队列的数据,出队列,从头部出去
public int getQueue() {
//判断队列是否空
if(isEmpty()) {
//抛异常
throw new RuntimeException("队列为空,不能取数据");//不用写return,因为throw也能返回
//System.out.println("test");//throw返回了,所以报错,unreachable不能到达
}
front++;//front后移
return arr[front];
}
//显示队列的所有数量
public void showQueue() {
//遍历
if(isEmpty()) {
System.out.println("空队列,无数据");
return;
}
for(int i=0;i<arr.length;i++) {
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
//显示队列的头数据个,注意不是取出数据
public int headQueue() {
//判断
if(isEmpty()) {
throw new RuntimeException("空队列,无头数据");
}
return arr[front+1];
}
}
注意:
用普通数组模拟队列只能使用一次,解决方法:环形队列
数组模拟环形队列:
对前面的数组模拟队列的优化,充分利用数组,因此将数组看作是一个环形的。***(通过取模的方式实现)***
分析说明:
1.尾索引的下一个为头索引时表示队列满**(空出一个位置作为约定)**—>(rear + 1) % maxSize == front 满
2.rear == front 空
使用数组模拟环形队列思路分析:
1.对front含义做调整,front指向第一个元素,即arr[front]---->队列第一个元素 front初始值为0
2.对rear含义做调整,rear指向最后一个元素的后一个位置,即arr[rear-1]---->队列最后一个元素**(空出一个位置作为约定)** rear初始值为0
3.当队列满时,条件是(rear + 1) % maxSize = front;【当front在0,rear在MaxSize-2的位置,预留一个位置,当rear+1即MaxSize-1对MaxSize-1取模=front即0时,为满】
4.当队列为空时,front = rear
5.当这样分析,队列中有效的数据个数:(rear + MaxSize -front) % MaxSize
6.我们可以在原来的队列上修改得到一个环形队列
//代码实现
import java.util.Scanner;
//数组模拟环形队列
public class ArrayToCircleQueue
{
public static void main(String[] args){
//测试一把
Scanner sc = new Scanner(System.in);
System.out.println("请输入需要创建的队列的长度:");
int len = sc.nextInt();
//创建队列
ArrayCircleQueue atcq = new ArrayCircleQueue(len+1);
char key = ' ';//接收用户输入
boolean loop = true;
//输出一个菜单
while(loop) {
System.out.println("请输入你需要进行的队列操作:");
System.out.println("a(add):添加数据到队列");
System.out.println("g(get):从队列中获取数据");
System.out.println("s(show):显示队列所有数据");
System.out.println("h(head):显示头数据");
System.out.println("e(exit):退出程序");
key = sc.next().charAt(0);//接收一个字符
switch (key) {
case 'a':
System.out.println("请输入一个需要添加的数据:");
int value = sc.nextInt();
atcq.addQueue(value);
break;
case 'g':
try {
int res = atcq.getQueue();
System.out.printf("获取的数据是:%d\n",res);
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case 's':
atcq.showQueue();
break;
case 'h':
try {
int res = atcq.showHead();
System.out.printf("队列头数据是:%d\n",res);
}catch(Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
sc.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
class ArrayCircleQueue
{
private int maxSize;
private int head;
private int rear;
private int[] arr;
public ArrayCircleQueue() {
// TODO Auto-generated constructor stub
}
//构造器
public ArrayCircleQueue(int arrMaxSize){
maxSize = arrMaxSize;
arr = new int[maxSize];
head = 0;
rear = 0;
}
//判断是否满
public boolean isFull(){
boolean res;
res = ((rear + 1)%maxSize == head);
return res;
}
//判断是否为空
public boolean isEmpty(){
return rear == head;
}
//添加数据到队列中
public void addQueue(int value){
//判断是否满
if(isFull()){
System.out.println("队列已经满,不能添加数据");
return;//结束方法
}else{
arr[rear] = value;//因为rear代表的是尾部的后一位,所以添加的时候,rear所指向的就是添加的位置
rear = (rear + 1) % maxSize;//添加成功,rear后移,考虑取模
}
}
//获取队列数据
public int getQueue(){
//判断队列是否空
if(isEmpty()){
//通过抛异常
throw new RuntimeException("队列空,不能取");
}else{
/*
这里需要分析出head是指向队列的第一个元素
1.先把head对应的值保存到一个临时变量
2.将head后移,考虑取模,否则容易数组越界
3.将临时保存的变量返回
*/
int value = arr[head];
head = (head+1) % maxSize;
return value;
}
}
//显示队列的数据
public void showQueue(){
//遍历
if(isEmpty()){
System.out.println("队列为空,没有数据");
return;//结束方法
}else{
/*思路:
从head开始遍历,遍历有效数据个元素
*/
for(int i = head;i<(head+size());i++){
System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);//i要取模
}
}
}
//求出当前队列有效数据个数
public int size(){
return (rear + maxSize - head)%maxSize;
}
//显示头元素
public int showHead(){
//判空
if(isEmpty()){
throw new RuntimeException("队列为空,没有头元素");
}else{
return arr[head];
}
}
}
System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);//i要取模
}
}
}
//求出当前队列有效数据个数
public int size(){
return (rear + maxSize - head)%maxSize;
}
//显示头元素
public int showHead(){
//判空
if(isEmpty()){
throw new RuntimeException("队列为空,没有头元素");
}else{
return arr[head];
}
}
}