数组模拟环形队列
前提条件:
front rear是角标值,从零开始,front=0,代表指向第一个数据。在一开始为空的时候,都指向同一个位置,都是0;
maxSzie = 真实数据长度 + 预留的一个空间。
最大容量顾名思义,
当到达满的时候,maxSzie指向 预留空间位置。
一.思路
1.分析
1.1
和上一篇内容不一样的是,front和rear的 指向内容需要做调整。
front 指向队列的__第一个元素_,不再是队列第一个元素之前的那个。front的初始值设置为:0
rear指向队列的最后一个元素的后一位,不再是指向队列的最后一个元素。rear的初始值设置为0
1.2 问题思考
队列满条件:(rear+1)%maxSize==front
队列空条件:front == rear
解析:队列满的条件为什么是这个?
rear +1 代表的是 队列放入的数据数量。
rear+1 可以这么理解: 一开始rear 和front的初始值为零,然后自增1 ,就代表队列里有一个数据。然后假设我一直往里面加数据,,(rear+1)%maxSize 代表我往里面加的数据是不是 maxSize的倍数,当你第一次 模的值为零,就代表这个队列满了。
再考虑我取数据的情况,我取一个数据,那么肯定front = front+1;等等,我取数据又不会导致队列满,只会释放空间,导致队列空 。极端点,一个已经满的 容量为 3 的队列,连续取了三次,我再往里面加,那么这个时候就是 front=3,rear=3;此时已经空了。
时刻记住front和rear指向的是哪里。
考虑一波这样的情况,容量maxSxize为 3 的队列,加两个数据,取一个数据,此时肯定是空,看看它是否逻辑自洽。 1 2 3
front == rear==0 是否为空? 是否满?
第一次加数据:0 1 否 否 1+1%3 !=0 发现公式没问题
第二次加数据 0 2 否 否 3%3 ==0 根据公式,此时应该已经满了?
预留了一个数据?
所以三个空间位置 ,我放两个就称之为 满了?
暂时只能这么理解了。
所以就是 ,为了让 (rear +1) %maxSize 这个东西是正确的多列满公式,rear的最大值其实是maxSize -1,这个队列的最大值其实是maxSize-1 .
1. 解析:队列空的条件为什么这么写
如果rear!= font,则 必然有空间存数据,就不是空的了,所以相等一定是空。
再在大脑里想象 一个队列。根据上面规定的 font 和rear 的指向位置,如果一开始不指向同一个位置,那肯定就是队列里有数据了,rear +1 了。
2. 解析:为什么 初始值都是 0?不再是-1?
不知道,目前的理解是 可以是-1,但是为了方便理解,采用 0
这样 0 == 0,也满足一开始队列为空的情况。
2.解析 为什么要预留一位?
答案:rear一直是自增,所以rear是有可能大于maxSize,%rear可以获得rear在环形队列上的真实位置。假设maxSize ==5,但是rear自增到10,此时rear 的实际位置是0
rear+1是预留一个位置,不牺牲这个空间会导致无法判断队列空还是满。
3.解析 为什么有效个数的公式是?
(rear-font+maxSize )%maxSize
这个其实是因为环形队列中,rear是有可能小于 font的,所以其实上面的公式类似与**|rear-font|%maxSize
原公式可以确保是一个正数模别人。
rear = maxSize -1
rear +1= maxiSize
这样就可以解释为什么 rear 是指向队列的最后一个元素的后一位,因为队列默认留一位来区分空还是满。
package com.lzp.datastructure.study.Day2Queue;
import java.util.Scanner;
/**
* @author lzp
* 循环队列的实现
*/
public class XunHuanArrayQueue {
public static void main(String[] args) {
System.out.println("测试数组模拟环形队列");
CircleArray arrayQueue = new CircleArray(4);
char key = ' ';//接受用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
//输出一个菜单
while (loop){
System.out.println("s(show):显示队列");
System.out.println("e(exit):退出队列");
System.out.println("a(add):添加数据到队列");
System.out.println("g(get):从队列取数据");
System.out.println("h(head):显示队列头部的数据");
key = scanner.next().charAt(0);//接受一个字符
switch (key){
case 's':
arrayQueue.showQueue();
break;
case 'a':
System.out.println("请输入一个数字");
int value = scanner.nextInt();
arrayQueue.addQueen(value);
arrayQueue.showQueue();
break;
case 'g' ://去除数据
try {
int res = arrayQueue.getQueen();
System.out.printf("取出的数据是%d\n",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int res = arrayQueue.hearQueue();
System.out.printf("队列头的数据是%d\n",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e'://退出程序
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("成功退出程序");
//根据测试,发现预留空间的角标是变化的。
}
}
class CircleArray{
private int maxSize; //循环队列最大值
private int front; //指向队列第一个元素,默认值零
private int rear; //指向队列最后一个元素的最后一位,默认为零。
private int[] arr; //该数组 用于存放数据,模拟队列
public CircleArray(int arrMaxSize){
maxSize =arrMaxSize;
arr = new int[maxSize];
}
//判断队列是否满
public boolean isFull(){
return (rear+1)%maxSize == front;
}
//判断队列是否为空
public boolean isEmpty(){
return front==rear;
}
//添加数据
public void addQueen(int n){// n是你传入的一个 int型数据。
//判断队列是否满
if(isFull()){
System.out.println("队列满了,不可以加入数据");
return;
}
//rear指向最后一位的后一位,所以可以直接往里面放,不用arr【rear+1】
arr[rear] = n;
//这里的rear肯定是要自增1的,两种情况,第一种rear自增后小于maxSize,第二种自增后大于等于maxSize
//因而需要取模 取模是为了循环数组下标
rear = (rear +1)%maxSize; //自增后的队列 rear=0, (0+1)% 8 = 1,自增后有效数据为1.
}
//取出数据
public int getQueen(){
//判断队列是否为空
if(isEmpty()){
//抛出异常
throw new RuntimeException("队列已经空,无法取到数据");//执行到这行,throw 就会return,throw下面的代码就已经无法执行了。
}
//1.取数据需要front自增,一般来说 取 arr【front】,但是你front后面还要自增,自增又需要考虑你这个front是不是指向数组最后一个元素
//2.因为front需要取模,所以直接先把front的值赋给一个变量返回出去。
int index = front;
front= (front +1 )%maxSize; //% 代表某种意义上的循环 类比上面的 rear的自增。 取模是为了防止角标越界
return arr[index];
}
//显示队列的所有数据
public void showQueue(){
//遍历
if(isEmpty()){
System.out.println("队列空的,没有数据");
return;
}
System.out.println("输出队列");
//从front开始遍历,遍历多少元素 ?遍历 有效 个元素\
// int c = (rear - front + maxSize) % maxSize;
for (int i = front; i < front + realSize(); i++) {
System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
}
}
//求出当前数据有效值个数
public int realSize(){
return (rear - front + maxSize) % maxSize;
}
// 显示队列的头数据 不是取数据 因为这是队列,所以头数据有特殊含义,因为队列取数据永远都是从取头数据的。
public int hearQueue(){
//判断
if(isEmpty()){
throw new RuntimeException("队列为空");
}
return arr[front];
}
}