三, Java 数组模拟实现普通队列,循环队列

三, 队列(Queen)

3.1 定义和栗子

  • 队列是一个有序列表,可以用数组或链表来实现;
  • 队列遵循先入先出的原则(先存入队列的数据先取出)
  • 队列存取的特点是尾部插入(用rear指示), 头部取出(用front指示).

3.2 数组模拟实现普通队列

3.2.1 数组模拟普通队列的实现思路

  • 数组模拟实现队列,图示如下:

front和rear的指向规则是由程序员自己定义的,在实现普队列中, 我们用font指向队列中第一个数据的前一个内存单元,而用rear指向队列的最后一个数据,这样的当我们插入数据或取出数据时,都需要先移动 front 或 rear,再执行插入或取出的动作.

  • 队列的满和空的判断:
  1. rear==front 说明队列为空
  2. rear= MAXSIZE-1 说明rear指向了数组的末尾,此时队列为满.
  • 最开始的状态数组索引区间为0到Maxsize-1, 长度为MaxSize, rear和front都指向起始位置 -1;
  • 插入元素时, 插入先判满, 队列未满则rear指针后移,索引+1, 指示一个空的数据单元,此时便可插入数据.
  • 取出元素时,取出先判空, 队列非空则front指针后移,索引+1, 此时标识着队列中数据的开始(即front+1 才是队列的第一个数据)

3.2.2 数组模拟普通队列的具体实现

package ArrayQueen;
//数组模拟队列的插入,取出,打印和取出头数据

import java.util.Scanner;

import static java.lang.System.exit;

public class ArrayQueen_copy{
    ///定义数组,以及队列的头标识和尾标识
    /我们在这里定义rear指向最后一个数据,front指向第一个数据的前一位;
    /在这里,数据的最大长度为MAX_SIZE, 索引范围 0~MAX_SIZE-1;
    private int MAXSIZE;
    private int[] array;///Q: 何时初始化?---->创建实例对象时,靠构造方法初始化
    private int front=-1;
    private int rear=-1;
    private int count =0;

    构造方法
    public ArrayQueen_copy(int size){
        array = new int[size];
        this.MAXSIZE=size;
        System.out.println("队列初始化已完成!");
    }
    ///工具方法
    //1. 判空
    public boolean isEmpty(){
        if (rear == front){
            return true;
        }
        return false;
    }
    ///2. 判满
    public boolean isFull(){
        if( rear == MAXSIZE-1){
            return true;
        }
        return false;
    }

    //Ⅰ 模拟队列的插入
    插入要判满! 满了直接返回
    ///队列插入,从尾部插入, rear先移动一个位置,再插入元素
    public void addQueen(int x){
        if(this.isFull()){
            System.out.println("队列数据已满, 无法存储!");
            return;
        }
        count++;
        System.out.println("队列总长度为: "+MAXSIZE+ ", 已插入数据的个数为: "+count);
        rear++;
        array[rear]=x;
        System.out.println("你需要插入队列的数据: "+x + "已经插入");
    }
    // Ⅱ, 模拟队列的取出
    //取出要判空!!!
    public int getQueen(){

        if (this.isEmpty()){
            System.out.println("队列为空,无法取出数据!");
            return 0;
        }
        front++;
        System.out.println(" front="+front);
        return array[front];
    }

    // Ⅲ, 直接打印队列的数据
    public void showQueen(){
        for(int i=0; i< array.length; i++){
            System.out.printf("\t arr[%d]=%d",i,array[i]);
        }
        System.out.println();
    }

    Ⅳ, 取出头数据
    public int getHead(){
        ///判空
        if (this.isEmpty()){
            System.out.println("队列为空,无法取出数据!");
            return 0;
        }
        return array[front];
    }

    public static void main(String[] args) {

        ArrayQueen_copy queen = null;
        /输出一个菜单(插入,取出,打印,得到头数据
        Scanner scanner = new Scanner(System.in);
        char ins =' ';
        boolean loop = true;
        while(loop){
            System.out.println("================菜单==================");
            System.out.println("a. 定义队列的长度");
            System.out.printf("b. 插入数据到队列 \t");
            System.out.printf("c. 取出队列的一个数据 \t");
            System.out.printf("d. 打印队列全部数据 \t");
            System.out.printf("e. 得到队列的头数据 \t \n");
            System.out.println("f. 退出本程序");

        //得到用户的指令
        ins = scanner.next().charAt(0);

        switch (ins){
            case 'a':
                System.out.println("请输入:");
                int size = scanner.nextInt();

                queen = new ArrayQueen_copy(size);
                break;

            case 'b':
                System.out.println("请输入:");
                int num = scanner.nextInt();
                queen.addQueen(num);
                System.out.println("插入动作已结束");
                break;

            case 'c':
                System.out.println("从队列中取出了元素: "+queen.getQueen());
                break;

            case 'd':
                queen.showQueen();
                break;

            case 'e':
                System.out.println("本队列的头数据为: "+queen.getHead());

            case 'f':
                System.out.println("程序已退出");
                exit(0);

            default:
                System.out.println("输入错误,请重新输入");
                break;
        }
        }

    }
}

3.3 数组模拟实循环队列

引言:
数组模拟实现普通队列的缺陷:

元素进队的时候, rear标识要向后移动;
元素出队的时候, front标识同样也要向后移动;
这样在经过了一系列的进队出队操作之后,rear和front都到达了数组的末尾即MAXSIZE-1这个索引处,虽然队列中已经没有元素了,但是仍然无法再插入数据, 这种所谓的"假溢出".

数组实现顺序队列的缺点是, 数组仅能使用一次,没有实现复用,所以我们可以借助模(%)运算将数组首尾连接起来, 达到对数组循环存取的目的.

3.3.1 数组模拟循环队列的实现思路

循环队列中遇到的问题:

Q1: 为什么循环队列总是浪费一个存储空间?

  • 如果不留出一个空白的存储空间的话, 当rear插入到最后一个数据时, rear和front都指向了队列的末尾, 此时rear==front, 跟队列的判空条件产生冲突;
  • 当然我们也可以不留空白存储空间,只不过我们要可以设置一个flag变量,当插入队列的最后一个数据时, 设置变量flag=1标识队列已满,不能再继续插入数据.

Q2: 如何计算循环队列中的个数

①, 一般来说, 元素插入空队列,rear标识率先移动, 所以rear的标识索引总是大于front, 此时队列中元素的个数为rear-front;
②, 但是,我们谈论的是一个循环队列嘛, 所以当rear移动完了一圈到了第二圈,而front可能还在第一圈, 这个时候front的索引就大于了rear, 此时队列中元素的个数为
Maxsize-(front-rear)=rear-front+Maxsize

综上我们可以总结计算循环队列元素个数(即有效值个数)的通用公式:
(rear-front+Maxsize)%MaxSize


  • 具体代码实现的思路:
  1. front的初始值为0, rear的初始值也为0
  2. 在循环队列中,我们可以自定义 front始终指向队列的第一个数据, 而rear总是指向队列末尾元素的下一个内存单元( 因为呀,我们需要在rear后空出一个空间作为约定).
  3. 循环队列中队空和队满的判断:
  • 循环队列: front==rear;
  • 循环队列: (rear+1)%MAXSIZE = front;
  1. 循环队列中有效数据的个数: (rear-front+Maxsize)%Maxsize;

3.3.2 数组模拟环形队列的具体实现

  • 完整代码如下:
package DataStrcture.ArrayDemo.arrayqueendemo;

import java.util.Scanner;

import static java.lang.System.exit;

public class ArrayQueen_copy_copy {
    /循环队列的实现(重要的是循环意识!)
    /*
        判空: rear == front;
        判满:  (rear+1)%MAXSIZE = front
        有效值的个数:   (rear-front+maxsize)%Maxsize
        我们自定义的初始状态: front指示着队列中的第一个数据,rear指向队列的最后一个数据的后一位

     */
    private int[] arr;
    private int rear;
    private int front;
    private int MAXSIZE;

    ///0.构造方法,初始化数组,初始化rear和front
    public ArrayQueen_copy_copy(int size){
        this.MAXSIZE = size;
        arr = new int[size];
        rear=0;
        front=0;
    }
    /0.工具方法, 判空判满
    public boolean isEmpty(){
        return rear==front;
    }

    public boolean isFull(){
        return (rear+1)%MAXSIZE == front;
    }

    1.插入,遍历输出,取得头数据,
    1.插入
    public void add(int num){
        ///插入前判满
        if(isFull()){
            System.out.println("循环队列中的元素已满!");
            return;
        }
        插入
        arr[rear]=num;
        rear = (rear+1)%MAXSIZE;
        System.out.printf("数据 %d yi插入到循环队列, 此时队列中的元素个数有 %d个 \n",num,(rear-front+MAXSIZE)%MAXSIZE);
    }
    2.取出一个数据
    public int getVal(){
        ///取出元素要判空
        if(isEmpty()){
            System.out.println("循环队列中已没有元素!");
        }
        ///取出
        int val = arr[front];
        front= (front+1)%MAXSIZE;
        return val;
    }
    /3.遍历输出循环队列
    public void showQueen(){
        ///首先我们要明确队列中的有效值个数
        int validCount = (rear-front+MAXSIZE)%MAXSIZE;
        ///然后我们还要知道,环形队列中数据在数组中存储的索引是动态的,所以数据总是从front标识的地方开始的!
        / int i=front;

        ///可以开始遍历啦
        System.out.println("循环队列遍历如下: ");
        最需要注意的地方,当我们循环遍历时,遍历的次数为元素的个数
        ///下面循环的遍历次数为 front+validCount-front = validCount;
        for(int i=front; i< front + validCount; i++){
            这里更是要注意,如果我们的循环队列数据从前往后存储在数组的后半段有一部分数组,在
            //前半段有一部分,这个时候我们就 还需要使用%运算去访问后半段的数据啦
            System.out.printf("\t Queen[%d]=%d",i%MAXSIZE,arr[i%MAXSIZE]);
        }
    }
    /4. 访问头数据
    public int getHead(){
        return arr[front%MAXSIZE];
    }

    测试类
    public static void main(String[] args) {
        ArrayQueen_copy_copy queen = null;
        /输出一个菜单(插入,取出,打印,得到头数据
        Scanner scanner = new Scanner(System.in);
        char ins = ' ';
        boolean loop = true;
        while (loop) {
            System.out.println();
            System.out.println("================菜单==================");
            System.out.println("a. 定义队列的长度");
            System.out.printf("b. 插入数据到队列 \t");
            System.out.printf("c. 取出队列的一个数据 \t");
            System.out.printf("d. 打印队列全部数据 \t");
            System.out.printf("e. 得到队列的头数据 \t \n");
            System.out.println("f. 退出本程序");

            //得到用户的指令
            ins = scanner.next().charAt(0);

            switch (ins) {
                case 'a':
                    System.out.println("请输入:");
                    int size = scanner.nextInt();

                    queen = new ArrayQueen_copy_copy(size);
                    System.out.println("初始化队列完成! ");
                    break;

                case 'b':
                    System.out.println("请输入:");
                    int num = scanner.nextInt();
                    queen.add(num);
                    System.out.println("插入动作已结束");
                    break;

                case 'c':
                    System.out.println("从队列中取出了元素: " + queen.getVal());
                    break;

                case 'd':
                    queen.showQueen();
                    break;

                case 'e':
                    System.out.println("本队列的头数据为: " + queen.getHead());

                case 'f':
                    System.out.println("程序已退出");
                    exit(0);

                default:
                    System.out.println("输入错误,请重新输入");
                    break;
            }
        }
    }
}

posted @ 2022-05-26 20:31  青松城  阅读(59)  评论(0编辑  收藏  举报