数据结构之循环队列的数组实现

一、概述

  先说下队列,一种先进先出的线性表,可以通过数组或者链表实现,这里将对数组实现的循环队列进行梳理。

  循环队列的定义借鉴下百度百科如下:

循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。
在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。
循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。

二、队列的基本操作

  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);
    }
}

 

 

 

 

    

  

  

 

posted on 2020-08-26 14:21  温柔的青竹十六夜  阅读(928)  评论(0)    收藏  举报