目录
【数组队列】
简介:队列类似于排队,其遵循先入先出的规律,队列需要头索引(front)与尾索引(rear),在不同的实现思路里头尾索引初始值不同,每添加一个数据尾索引向后一位,每取出一个数据头索引向后一位。是一个有序列表,可以用数组或链表为核心存储介质实现。
简易数组队列实现
结构分析:
1,需要先初始化front与rear为-1,后续front指向第一个数据,rear指向最后一个数据,在创建一个maxSize大小的数组arr
2,isEmpty方法通过front==rear判断,isFull方法通过rear==maxSize-1判断
3,add方法先判断isFull,再通过arr[++rear]来实现添加数据和尾索引向后一位
4,get方法先判断isEmpty,再通过返回arr[++front]来实现返回数据和头索引向后一位
5,show方法先判断IsEmpty,再通过从front到arr.length循环遍历输出arr中数据
6,head方法先判断IsEmpty,再通过arr[front+1]数据,输出!=取出。
package cn.dataStructure.demo;
import javax.sound.midi.Soundbank;
import java.util.Scanner;
class ArrayQueue{
private int maxSize;//队列数组最大值存储空间
private int front;//头索引,保存头数据的前一个索引
private int reer;//尾索引,保存尾数据的后一个索引
private int arr[];//保存数据
public ArrayQueue(int maxSize){
front=-1;//初始化front与reer为-1
reer=-1;
this.maxSize=maxSize;
arr=new int[maxSize];
}
public boolean isEmpty(){
return this.front==this.reer;
}
public boolean isFull(){
return this.reer==maxSize-1;
}
public void add(int data){
if (isFull()){
throw new RuntimeException("队列满,无法添加数据");
}
this.arr[++reer]=data;//reer向后一位
}
public int get(){
if (isEmpty()){
throw new RuntimeException("队列空,无法获取数据");
}
return this.arr[++front];//front向后一位
}
public void show(){
if (isEmpty()){
System.out.println("队列空");
return;
}
for (int i=front+1;i<arr.length;i++){//从有数据的开始展示
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
public int head(){//展示头数据,不是取出数据,front不加一
if (isEmpty()){
throw new RuntimeException("队列空");
}
return arr[front+1];
}
}
public class 队列_queue {
public static void main(String[] args) {
ArrayQueue queue=new ArrayQueue(3);
boolean flag=true;//循环判据
char key;//用户输入
Scanner scan=new Scanner(System.in);
while (flag){
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=scan.next().charAt(0);//接收一个字符
switch (key){
case 's':
queue.show();
break;
case 'a':
System.out.print("输入一个数:");
int data=scan.nextInt();
queue.add(data);
break;
case 'g':
try {
System.out.println(queue.get());
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try {
System.out.println(queue.head());
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
flag=false;
scan.close();//关闭输入流
break;
}
}
System.out.println("已退出程序");
}
}
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
a
输入一个数:1
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
a
输入一个数:2
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
s
arr[0]=1
arr[1]=2
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
h
1
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
简易版本的最大不足之处在于无法复用,数组用过一遍,由于reer与front直加,超出数组范围,数组失效。采用循环数组(环状数组)的形式可实现复用
循环数组队列实现
结构分析:
1,需要先初始化front与rear为1,后续front指向第一个数据,rear指向最后面一个数据之后一个索引(这是为了空出最后一个一个数组空间,这个空闲空间很有用,后续会讲),再创建一个maxSize大小的数组arr
2,队列满判据:(rear+1)%maxSize==front (reer是索引,maxSize是个数,索引从0开始,个数从1开始,所以rear+1后获得与个数对应的值,再与maxSize求模得到循环过程中一个周期的真实位置索引,当其等于front时,说明周期完成,即数据满了)这里不好理解,可画图验证。
3,队列空判据:rear==front
4,队列中有效值个数:(rear+maxSize-front)%maxSize(不好说清楚,画图理解)
5,通过以上判据,方法对简易数组队列进行修改,注意循环数组中数据在循环周期里的真实位置索引,要通过n%length的形式来获取。
package cn.dataStructure.demo;
import javax.sound.midi.Soundbank;
import java.util.Scanner;
class ArrayQueue{
private int maxSize;//队列数组最大值存储空间
private int front;//头索引,保存头数据的前一个索引
private int rear;//尾索引,保存尾数据的后一个索引
private int arr[];//保存数据
public ArrayQueue(int maxSize){
front=0;//初始化front与rear为0
rear=0;
this.maxSize=maxSize;
arr=new int[maxSize];
}
public boolean isEmpty(){
return this.front==this.rear;//空判据:front=rear
}
public boolean isFull(){
return (rear+1)%maxSize==front;//满判据:(rear+1)%maxSize==front
}
public void add(int data){
if (isFull()){
throw new RuntimeException("队列满,无法添加数据");
}
this.arr[rear]=data;//rear从0开始
rear=(rear+1)%maxSize;//rear向后一位
}
public int get(){
if (isEmpty()){
throw new RuntimeException("队列空,无法获取数据");
}
int value=arr[front];//front从0开始
front=(front+1)%maxSize;//front向后一位
return value;
}
public void show(){
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]);//i的真实位置索引需要求模计算
}
}
public int size(){//计算有效值个数
return (rear+maxSize-front)%maxSize;//有效值个数判据:(rear+maxSize-front)%maxSize
}
public int head(){//展示头数据,不是取出数据,front不加一
if (isEmpty()){
throw new RuntimeException("队列空");
}
return arr[front];
}
}
public class 队列_queue {
public static void main(String[] args) {
ArrayQueue queue=new ArrayQueue(4);
boolean flag=true;//循环判据
char key;//用户输入
Scanner scan=new Scanner(System.in);
while (flag){
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=scan.next().charAt(0);//接收一个字符
switch (key){
case 's':
queue.show();
break;
case 'a':
System.out.print("输入一个数:");
int data=scan.nextInt();
queue.add(data);
break;
case 'g':
try {
System.out.println(queue.get());
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try {
System.out.println(queue.head());
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
flag=false;
scan.close();//关闭输入流
break;
}
}
System.out.println("已退出程序");
}
}
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
a
输入一个数:1
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
a
输入一个数:2
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
s
arr[0]=1
arr[1]=2
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
h
1
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
s
arr[0]=1
arr[1]=2
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
g
1
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
a
输入一个数:4
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
s
arr[1]=2
arr[2]=4
s(show):显示队列
a(add):添加数据
g(get):取出数据
h(head):查看头数据
e(exit):退出程序
可见,循环数组实现了数组的复用。但其困难之处在于%(求模)的理解,求模运算还是为了将多个循环周期里的索引压缩到一个周期中的索引,好与其他值比较。另外解答开始提出的循环数组队列中为什么要将数组最后一个位置空出,让rear指向?
队列里有两个索引:
一个指向队列头front,一个指向队列尾rear。每次增加一个队列元素,rear+1。每次出队一个元素 front +1。
队列的长度大小就是rear-front。
但是在循环队列中,如果还是用rear-front,就会有一个问题出现,就是两个指针指向同一个的时候,你无法区分是因为队列是空的,还是rear已经绕了一圈回来了(即队满的情况)。so我们要加一个空的位置,这个位置不放任何元素,作用就是为了区别队空和队满。如果队空的情况下,rear=front。
队满的情况下,rear-front!=0,得出的答案是真正的队列长度。也就是说,若不空出此位置将导致无法用 rear==front 的判据来判定是否队列为空。(理解不了,画图吧)
【数据结构与算法整理总结目录 :>】<-- 宝藏在此(doge)