【小白学算法】4. 循环队列

简介: 【小白学算法】4. 循环队列

一、假溢出


这是因为数组的末尾已经被占用了,入队会继续在数组后面增加,于是产生数组越界。


但是实际上,数组里是有空闲位置的,这种也可以叫“假溢出”。


1268169-20210313211154071-357742303.png


为了解决“假溢出”的问题,于是乎有了循环队列。


既然数组后面满了,头部有空,那继续加进来的元素从头开始放即可。


接着上图,这时候有a6入队,于是rear的下标指向a6的下一个元素位置,看起来没什么问题。


1268169-20210313211241040-576390048.png


但是继续有新的元素a7入队的时候,因为front一直没变,这时候rear指针跟front就重合了,也就是说此时front=rear


1268169-20210313211452152-27254263.png


可是在上一章的代码里,我们是用front=rear来判断是否为空数组的。


// 判断队列是否为空
    public boolean isEmpty() {
        return rear == front;
    }


现在是相等了,条件满足,但是数组是满的。


二、循环队列判断是空是满


这种情况看起来确实比较辣手,那如果不让这种情况出现不就可以了么?


假设,我们让数组始终保留一个元素空间,也就是让rear指向队列中最后一个元素的后一个位置。


比如,当a6放入之后,此时就当做队列已经满了,这样的话就不会出现上述的情况了。


1268169-20210313211628336-1135799305.png


为了实现这种思路,front也需要做调整。在上一章中,front初始位置是指在了队头元素的前一个,现在我们让它就指在


第一个元素。队列的最大尺寸还是maxSize,那么,现在判断队列满的条件就可以是这样(rear+1)%maxSize = front


验证一下,下面的2种队列满情况:


1268169-20210313211043774-1991219196.png


  • 左图:队列中,maxSize=5,front=0,rear=4,判断(4+1)% 5=0=front,队列已满
  • 右图:队列中,maxSize=5,front=2,rear=1,判断(1+1)% 5=2=front,队列已满

继续验证一下,队列没满的情况:


1268169-20210313213850535-1189249998.png


  • 队列中,maxSize=5,front=2,rear=0,判断(0+1)% 5=1≠front,队列未满


三、循环队列的长度计算


队列的长度,也就是说队列中实际存了多少个元素。


此时需要考虑rear与front之间的三种情况:


  1. rear=front
  2. rear>front
  3. rear<front


第一种自然都清楚,队列为空。


第二种,rear>front,此时队列的长度为:rear-front


1268169-20210313215935809-698188075.png


第三种,rear<front,比较复杂些。


队列长度分为2段:一段是maxSize-front,另一段则是:0+rear(结合右图理解)。故此时队列总长度为两段相加:rear-front+maxSize


1268169-20210313220542652-91811693.png


所以队列长度的通用计算公式为:(rear-front+maxSize)% maxSize


四、代码实现


既然现在队列是满是空的判断条件有了,队列长度也能计算出来了,那么代码就好改造了(基于上一章),变成循环队列。


package circle;
import java.util.Scanner;
public class CircleArrayQueue {
    public static void main(String[] args) {
        System.out.println("-----测试循环队列-----");
        // 创建一个队列
        CircleArray circleArray = 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':
                    circleArray.showQueue();
                    break;
                case 'a':
                    System.out.println("请要添加的数");
                    int value = scanner.nextInt();
                    circleArray.addQueue(value);
                    break;
                case 'g':
                    try {
                        int res = circleArray.getQueue();
                        System.out.printf("取出的数据是:%d", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int headValue = circleArray.showHeadQueue();
                        System.out.printf("队首数据是:%d", headValue);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop = false;
                    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];
        front = 0;
        rear = 0;
    }
    // 判断队列是否已经存满
    public boolean isFull() {
        return (rear + 1) % maxSize == front;
    }
    // 判断队列是否为空
    public boolean isEmpty() {
        return rear == front;
    }
    // 添加数据到队列
    public void addQueue(int num) {
        // 判断队列是否满了
        if (isFull()) {
            System.out.println("队列已满,不可加入数据");
            return;
        }
        arr[rear] = num;
        // 将rear后移,要考虑取模
        rear = (rear + 1) % maxSize;
    }
    // 拿出队列数据
    public int getQueue() {
        // 判断队列是否空
        if (isEmpty()) {
            // 抛出异常
            throw new RuntimeException("队列为空,不可取数据");
        }
        int tempValue = arr[front];
        front = (front + 1) % maxSize; //也要考虑取模,防止front不停的增加,导致越界
        return tempValue;
    }
    // 显示队列所有数据
    public void showQueue() {
        // 遍历
        if (isEmpty()) {
            System.out.println("队列为空");
            return;
        }
        // 从front开始遍历,遍历多少个实际存的元素即可
        for (int i = front; i < front + CircleQueueSize(); i++) {
            System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
        }
    }
    // 求出当前队列有效数据的个数
    public int CircleQueueSize() {
        return (rear - front + maxSize) % maxSize;
    }
    // 显示队里的队首数据
    public int showHeadQueue() {
        if (isEmpty()) {
            // 抛出异常
            throw new RuntimeException("队列为空,不可取数据");
        }
        return arr[front];
    }
}


重点的改动在于取模。

相关文章
|
3月前
|
存储 算法
【数据结构和算法】--队列的特殊结构-循环队列
【数据结构和算法】--队列的特殊结构-循环队列
24 0
|
1月前
|
算法
【数据结构与算法】循环队列
【数据结构与算法】循环队列
13 0
|
3月前
|
存储 算法 调度
【数据结构与算法】详解循环队列:基于数组实现高效存储与访问
【数据结构与算法】详解循环队列:基于数组实现高效存储与访问
|
3月前
|
存储 算法
数据结构和算法学习记录——设计循环队列(数组实现循环队列)核心思路、题解过程、完整题解
数据结构和算法学习记录——设计循环队列(数组实现循环队列)核心思路、题解过程、完整题解
31 1
|
4月前
|
算法
数据结构与算法之循环队列的操作
数据结构与算法之循环队列的操作 /* 循环队列的入队和出队算法设计 初始化循环队列 、打印队列、插入元素到循环队列、获取循环队列的首元素,元素不出队、出队、获取循环队列元素个数、判断循环队列的空和满。
50 0
【数据结构和算法】认识队列,并实现循环队列
【数据结构和算法】认识队列,并实现循环队列
|
算法
【算法入门】设计模板队列|循环队列(下)
【算法入门】设计模板队列|循环队列
87 0
|
算法 UED
【算法入门】设计模板队列|循环队列(上)
【算法入门】设计模板队列|循环队列
81 0
|
存储 算法
【数据结构与算法】设计循环队列
【数据结构与算法】设计循环队列
|
前端开发 算法
【数据结构与算法】队列-模拟实现队列以及设计循环队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。 队列是一种先进先出的数据结构,注意和栈进行区分,不要记混.