数组实现循环队列
前言
程序实现是Java语言。本文仅个人理解,仅供参考。若有不正之处,欢迎评论指出!
一、队列
队列是个什么呢?队列是一种数据的存储结构,具有先入先出,后入后出的特点。
举几个例子(虽然可能有点····):第一个就是我们人的吃与拉!这就是一个典型的队列,今天吃的与明天吃的相比较,今天的消化之后的**肯定比明天先排出!!!
还有就是银行医院等的排号办理业务,排在前面的先办理,办理完毕走人,然后才轮到后面的人办理.
图大概就如下(取元素先取队头方向的,添加元素从队尾方向加入):
二、循环队列
1.概念:
当出队一个元素头指针向后移动一位,入队一个元素尾指针就同样向后移动一位。这里的后可以理解成:顺时针方向。
如图:成环状的队列,就是循环队列,一般约定队尾指向的位置为空,不存数据,至于那些前辈大佬们为什么这么约定,下面再说说我的理解。出队的元素总是靠近对头方向的,入队元素总是从靠近队尾方向入的。
2.具有的一些特点,以及对这些特点的看法理解
初始化:front = rear = 0;
队列空:根据初始化的状态就是队列空的状态,所以就得出:front = rear的时候就是队列空的时候。
队列满:(rear + 1)% maxsize = front这相当于就是判断队列是否为空的公式吧,为什么呢?maxsize代表的是数组的长度,rear与front按照数组的下标来看,我们的约定是rear指向的地方不存值,那么根据约定来,“存满”就是rear加上一个1 的后等于front,这个没毛病。
那么问题来了,为啥留空呢?还要加1,浪费空间又麻烦?不是的,根据队列空的状态来看,假如没有这个约定,那么队列满的状态(先抛开maxsize不管)也是front = rear!是不是冲突了?所以还是有作用的。
那么对那个maxsize取余的目的是啥?因为它是一个环状的,定长的,所以就是为了避免加1后越出数组的下标边界,所以才对数组长度进行取余,避免了这个错误,也不改变结果。
判断队列中现存的有效的元素个数:(rear - front +maxsize) % maxSize,有些地方写成了:(rear +maxsize - front) % maxsize,但是为了方便讲解我的理解(为啥这样写)就看我的那个吧。
首先,我们单从maxsize来看,把rear-front看成一个正数(其实结果按理本来也就应该是一个正数,因为数组没有负数下标),我们对于:一个正数加上某个正数以后,再对和 – 取余 --加上的这个数,其结果与不加是一样的,然而当这个数是负数的时候,其结果恰好是为正数时的相反数,理解起来没任何毛病对吧?其实随便自己举两个例子就知道了。再来一句!个数不可能为负数!这样就懂了吧?看似“多此一举”其实不然,是为了避免出现负数个数的情况,
最后!上代码!(示例):
import java.util.Scanner;
/**
* @program: 环形队列
* @description: 环形队列实验
* @author: Mr.XiaoShi
* @create: 2020-12-24 21:01
**/
public class CircularQueue {
public static void main(String[] args) {
CircularQueue cs = new CircularQueue(8);
Scanner sc = new Scanner(System.in);
while (true){
System.out.println("0、插入\t1、取出\t2、查看队头\t3、遍历队列\t4、有效个数\t5、退出");
/*手动捕获自己抛出的异常*/
try {
switch (sc.next()){
case "0":System.out.print("请输入一个整数:");break;
case "1":System.out.println("对头元素:"+cs.dequeue()+"已取出!");break;
case "2":System.out.println("对头元素为:"+cs.getQueueHead());break;
case "3":System.out.println("当前队列元素:\n");cs.queueShow();break;
case "4":System.out.println("队列有效元素个数为:"+cs.getEffectiveNumber());break;
case "5":System.out.println("退出程序!");return;
}
}catch (Exception e){
System.out.println(e.getMessage()); //输出异常,提示!
}
}
}
private int maxSize; //最大容量
private int rear; //尾指针
private int front; //头指针
private int [] queue; //队列
/*构造器:根据传入长度创建数组*/
public CircularQueue(int maxSize) {
this.maxSize = maxSize;
queue = new int[maxSize];
}
/*队空*/
public boolean queueEmpty(){
return rear == front;
}
/*队满*/
public boolean queueFull(){
return (rear +1 ) % maxSize == front;
}
/*查看有效元素个数*/
public int getEffectiveNumber(){
return (rear - front +maxSize) % maxSize;
}
/**
*@Description: 查看对头元素,但不取出
*@Param:
*@return: front指向的(int)元素
*@Author: your name
*@date: 2020/12/24 0024
**/
public int getQueueHead(){
if (queueEmpty()){
throw new RuntimeException("队列空"); //手动抛出异常,既能结束方法,又能提示出错误信息
}
else
return queue[front];
}
/**
*@Description: 完成入队操作
*@Param: element:元素的意思,插入的元素
*@return: void
*@Author: your name
*@date: 2020/12/24 0024
**/
public void enqueue(int element){
if (queueFull()){
throw new RuntimeException("队列满,插入失败!"); //抛出异常
}
queue[rear++] = element;
rear %= maxSize;
}
/**
*@Description: 完成出队操作
*@Param:
*@return: int类型的当前对头指向元素
*@Author: your name
*@date: 2020/12/24 0024
**/
public int dequeue(){
if (queueEmpty()){
throw new RuntimeException("队列空");
}
int tmp = queue[front++];
front %= maxSize;
return tmp;
}
/**
*@Description: 遍历队列元素,但不取出
*@Author: your name
*@date: 2020/12/24 0024
**/
public void queueShow(){
if (queueEmpty()){
throw new RuntimeException("队列空");
}
for (int i = front; i < front + getEffectiveNumber(); i++) {
System.out.println("queue["+i % maxSize+"] = "+queue[i % maxSize]);
}
}
}