数据结构之循环队列的数组实现
一、概述
先说下队列,一种先进先出的线性表,可以通过数组或者链表实现,这里将对数组实现的循环队列进行梳理。
循环队列的定义借鉴下百度百科如下:
循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。
在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。
循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。
二、队列的基本操作
1、初始化
2、判断队列是否为空
3、判断队列是否已满
4、获取当前队列长度
5、入队
6、出队
三、实现约定
基于数组实现的循环队列,我们做如下的定义和约定:
1、有一个指向队头元素下标的指针,记为front
2、有一个指向队尾元素的后一个位置下标的指针,记为rear
3、初始化构造队列时,front == rear == 0
4、入队操作时,front指针增1
5、出队操作时,rear指针增1
四、问题点梳理
按照上述约定时,结合队列基本操作,在实现时,有如下两个问题需要思考解决:
问题一:
如何判断队列的状态,是空,或者已满呢?
用front == rear显然是不行的,按照之前的约定,当队列为空,队列已满时,都满足与此情况。
针对这个问题,有如下三种解决方案:
1、使用一个变量num记录当前队列中的元素长度,当num == maxSize && front == rear时,队列已满,否则队列为空;
2、使用一个变量flag记录元素状态,当队列中有值,flag =true,当 flag == true && front == rear时,队列已满;
3、约定队列最多可放 maxSize - 1 个元素,当 (rear + 1)% maxSize == front时,队列已满,front == rear ,队列为空。
第三种比较通用,教材也是以此来举例,所以本文也用第三种方法进行实现。
补充说明:
第三种中的(rear + 1)% maxSize == front 来判断队列已满,可以这样理解
队列满存在两种情况:
1)real > front
2)real < front
以maxSize == 5 为例
当队列满时 ,real > front ,则 front == 0 ,real == 4,(4+1)% 5 == 0
real < front,取一种情况,real == 1 ,front == 2,(1+1)% 5 == 2
问题二:
如何计算当前队列元素长度呢?
不考虑循环,那么rear永远大于front,则队列长度 queueLength = rear - front
若循环队列,则存在 rear < front 的情况,此时,该如何计算队列长度?
举个例子,若maxSize = 5,front = 3,rear = 1,此时,队列应该在长度为5的数组中,共有三个元素,如下图:

假如不考虑循环因素,该队列在普通数组中应该表示如下:

如上图所示,front = 3 ,rear = 6 ,此时元素个数为 rear -front = 3
为了区分,我们将图一中的 rear 称为 r1 ,图二中的 rear 为 r2 ,对比可知,r2 = r1 + maxSize
则 r2 -front = 3 可转换为 r1 + maxSize - front = 3 ,得公式 queueLength = rear + maxSize - front ,此时公式适用于 rear < front 的情况。
综合上述两种情况,得 queueLength = ( rear + maxSize - front )% maxSize
五、代码实现
通过java实现,代码如下:
public class CircleQueue { public static void main(String[] args){ Scanner scanner = new Scanner(System.in); CircleArrayQueue circleArrayQueue = new CircleArrayQueue(10000); circleArrayQueue.add(10); circleArrayQueue.add(20); circleArrayQueue.add(30); circleArrayQueue.add(40); System.out.println("1:添加"); System.out.println("2:删除"); System.out.println("3:打印当前队列"); System.out.println("4:打印数组"); System.out.println("5:退出"); boolean flag = true; while(flag){ System.out.println("请输入操作:"); int orea = scanner.nextInt(); switch (orea){ case 1 : System.out.println("请输入入队数字:"); int val = scanner.nextInt(); try { circleArrayQueue.add(val); }catch (Exception e){ System.out.println("添加失败,当前队列已满!"); } break; case 2 : try { int reduce = circleArrayQueue.reduce(); System.out.println("出队数字:" + reduce); }catch (Exception e){ System.out.println("出队失败,当前队列为空!"); } break; case 3 : circleArrayQueue.printCurrentQueue(); System.out.println(); break; case 4 : System.out.println(circleArrayQueue.toString()); break; case 5 : flag = false; System.out.println("BYE!"); break; default: System.out.println("请输入指令:1、2、3、4"); break; } } } } /** * 数组实现的队列 * 简单一次性数组 */ class CircleArrayQueue{ // 最大长度 private int maxSize; // 头部指针 // 头部指针指向头部元素,并且初始化为0 private int front; // 尾部指针 // 尾部指针指向尾部元素的后一个位置,并且初始化为0 private int rear; // 数组容器 private int[] array; /** * 构造方法 * @param maxSize */ public CircleArrayQueue(int maxSize){ this.maxSize = maxSize; front = 0; rear = 0; array = new int[maxSize]; } /** * 入队操作 * 入队时,队尾指针加一 * @param value * @return * @throws RuntimeException */ public int add(int value) throws RuntimeException{ // 先判断队列是否已满 if (isFull()){ throw new RuntimeException("the queue is full!"); } // array[++rear] = value; array[rear] = value; // 循环队列,这里需要取余,否则数组越界 rear = (rear + 1) % maxSize; // 返回当前队列长度 return currentQueueSize(); } /** * 计算当前队列的长度 * @return */ private int currentQueueSize() { /* 这里计算数组元素,需要考虑两种情况 1、rear >= front 2、rear < front (循环数组) 情况一,元素个数为 rear - front 情况二,元素个数为 rear + maxsize -front 为了满足上述两种情况,故产出如下式子: queueLength = (rear + maxSize - front) % maxSize; */ return (rear + maxSize - front) % maxSize; } /** * 队列是否已满 * 当front == rear时,存在两种情况:队列为空,或者队列已满 * 有三种情况可以解决这个情况 * 1)记录当前数组长度num,当num == maxsize,并且front == rear时,表示数组已满 * 2)记录一个标识位 flag,当flag == true,并且front == rear时,表示数组已满 * 3)数组空一个元素,当front == rear时,表示数组已空,当 rear + 1 == front表示数组已满 * 下面将用第三种方式 * @return */ private boolean isFull() { // 这里必须取余,因为时循环队列 if ((rear + 1) % maxSize == front) { return true; } return false; } public int reduce() throws RuntimeException{ // 先判断队列是否已空 if(isEmpty()){ throw new RuntimeException("the queue is empty!"); } int result = 0; result = array[front]; // 清空当前队列元素 array[front] = 0; front = (front + 1) % maxSize; return result; } /** * 判断队列是否为空 * @return */ private boolean isEmpty() { // 当队头和队尾指针相同时,则队列元素为空 if (front == rear){ return true; } return false; } /** * 打印当前队列 */ public void printCurrentQueue(){ if (isEmpty()){ System.out.println("当前队列为空!"); return; } // 思路: // 从front开始向后取,取size个元素 // 每回取值时i++ // 但是,由于时循环队列,故,i对应数组下标不为i,而为 (i % maxSize) for(int i = front ; i < front + currentQueueSize() ; i++){ System.out.print(array[(i % maxSize)] + " "); } } @Override public String toString(){ return Arrays.toString(array); } }
浙公网安备 33010602011771号