队列(顺序存储)

队列

基本介绍

  1. 队列是一个有序列表,可以用数组或是链表来实现。

  2. 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出

  3. 示意图:(使用数组模拟队列示意图)

  4. 在这里插入图片描述

数组模拟队列思路

  1. 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 MaxSize 是该队
    列的最大容量。

  2. 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标,
    front 会随着数据输出而改变,而 rear 则是随着数据输入而改变,如上图所示。

  3. 代码实现及测试

    public class ArrayQueue {
    
        private int[] arr;// 该数组用于存放数据
        private int front; // 指向队列第一个数据前一个位置
        private int rear; // 队列尾
        private int MaxSize;// 队列的最大容量
    
        public ArrayQueue(int ArrMaxSize){
            MaxSize = ArrMaxSize;
            arr = new int[MaxSize];
            front = rear = -1;
    
        }
    
        //判断队列是否为空
        public boolean isEmpty(){
            return front == rear;
        }
    
        //判断队列是否为满
        public boolean isFull(){
            return rear == MaxSize-1;
        }
    
        //入队
        public void add(int data){
            if (isFull())
                throw new RuntimeException("队列满,无法加入该数据"+data);
            rear++;
            arr[rear] = data;
            System.out.println("添加成功");
        }
    
    
    
        //显示队列的所有数据
        public void showQueue(){
    
            if (isEmpty()){
                System.out.println("队列空的,没有数据");
                return;
            }
            for (int i = front + 1; i < arr.length; i++) {
                //这里要从front+1的位置开始遍历,因为front指向的是出队的元素,我们要从下一个位置开始遍历
                System.out.printf("arr[%d]=%d\n",i,arr[i]);
            }
            
        }
    
        //出队
        public int getQueue(){
            if (isEmpty())
                throw new RuntimeException("队列为空,无法出队");
            front++;
            System.out.println("已出队");
            return arr[front];
    
        }
    
        //显示队列头的元素
        public int headQueue(){
    
            if (isEmpty()){
                throw new RuntimeException("队列空的, 没有数据");
            }
            return arr[front + 1];
    
        }
    
    }
    //使用数组模拟队列
    public class ArrayQueueDemo {
    
        public static void main(String[] args) {
            ArrayQueue arrayQueue = new ArrayQueue(5);
            boolean flag = true;
            Scanner sc = new Scanner(System.in);
            char key = ' ';//接收用户输入
            while (flag){
                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 = sc.next().charAt(0);//接收一个字符
                switch (key){
                    case 's':
                        arrayQueue.showQueue();
                        break;
                    case 'a':
                        System.out.println("请输入要入队的数据");
                        try {
                            int value = sc.nextInt();
                            arrayQueue.add(value);
                        }catch (Exception e){
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'g':
                        try {
                            int d = arrayQueue.getQueue();
                            System.out.println("取出的数据是: "+d);
                        }catch (Exception e){
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'h':
                        try {
                            int h = arrayQueue.headQueue();
                            System.out.println("队列头部的数据是: "+h);
                        }catch (Exception e){
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'e':
                        sc.close();
                        flag = false;
                        break;
                    default:
                        break;
                }
                System.out.println("======================");
            }
        }
    
    }
    
  4. 问题分析及优化思路

    1. 目前数组使用一次就不能用, 没有达到复用的效果

    2. 将这个数组使用算法,改进成一个环形的队列 取模:%

数组模拟环形队列

  1. 环形队列思路分析

    1. 对前面的数组模拟队列的优化,充分利用数组. 因此将数组看做是一个环形的。(通过取模的方式来实现即可)

    2. front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素

      front 的初始值 = 0

    3. rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.

      rear 的初始值 = 0

    4. 当队列满时,条件是 (rear + 1) % maxSize == front 【满】

    5. 当队列为空的条件, rear == front 空

    6. 队列中有效的数据的个数 (rear + maxSize - front) % maxSize // rear = 1 front = 0

    7. 我们就可以在原来的队列上修改得到,一个环形队列。

  2. 代码实现及测试

    public class CircleArrayQueue {
    
        private int[] arr;// 该数组用于存放数据
        private int front; // 指向队列第一个数据的位置
        private int rear; // 指向队列最后一个数据的后一个位置
        private int MaxSize;// 队列的最大容量
    
        public CircleArrayQueue(int ArrMaxSize){
            MaxSize = ArrMaxSize;
            arr = new int[MaxSize];
    //        front = rear = 0;  因为类变量默认为0 , 所以可以不写这句
        }
    
        //判断队列是否为空
        public boolean isEmpty(){
            return front == rear;
        }
    
        //判断队列是否为满
        public boolean isFull(){
            return (rear + 1) % MaxSize == front;
            /*
            我个人的理解是:   如果这个队列是满的,那么由于空出来了一个位置作为约定,所以
             1、 当rear在front上面(即比front的值大)的时候,rear一定是在数组的最后一个位置(下标为MaxSize-1),并且front在下表为0的位置,
                 所以rear+1的值就是MaxSize,于是(rear + 1) % MaxSize = 0,即rear+1的值等于数组的最大空间数,取模所得的结果也就是front的值;
    
             2、 当rear在front下面(即比front的值小)的时候,rear的下一个位置一定是front(因为数组满的时候只可能空一个位置,也就是rear所在的位置),
                 并且rear+1也一定是小于MaxSize的,所以(rear + 1) % MaxSize = 它自己(rear + 1),也就是front
    
             3、 因此,当队列满的时候,(rear + 1) % MaxSize == front是一定成立的。
             */
        }
    
        //入队
        public void add(int data){
            //要入列, 先得判断队列是否满了
            if (isFull())
                throw new RuntimeException("队列满,无法加入该数据"+data);
            arr[rear] = data;
            System.out.println("添加成功");
            //注意rear不能是简单的++,要考虑超出数组范围的情况, 最终效果还是取到rear的下一位(是在一个循环里)
            rear = (rear + 1) % MaxSize;
        }
    
        //显示队列的所有数据
        public void showQueue(){
    
            //要遍历,先得判断队列是否为空
            if (isEmpty()){
                System.out.println("队列空的,没有数据");
                return;
            }
            for (int i = front; i < front + size(); i++) {
                //因为i从front开始,如果rear比front的值小的话,就只能遍历到末尾,而0到rear就遍历不到,并且i的值会越界
                //所以将i % MaxSize后, 即便是i大于MaxSize, 取完模就会回到前面的数值。
                System.out.printf("arr[%d]=%d\n",i % MaxSize,arr[i % MaxSize]);
            }
    
        }
    
        //获取有效数据的个数
        public int size(){
            return (rear + MaxSize - front) % MaxSize;
            /*
            我个人的理解:
                1、 当rear的值比front大的时候, rear - front 就是数组中有效数据的个数(这是显而易见的,注意rear所在的位置是空的);
                2、 当rear的值比front小的时候, MaxSize - front 即是从front到MaxSize-1位置的有效数据个数, 然后rear所在的下标位置
                    就是从队列头部到rear的有效数据个数, 因此rear + MaxSize - front就是两个的和,即总有效数据个数
                3、 而对于1、中的情况, 将rear - front加上MaxSize再取模之后结果还是rear - front, 没有影响, 所以有效数据的个数就是
                 rear + MaxSize - front
             */
        }
    
        //出队
        public int getQueue(){
            //要出队, 先判断队列是否为空
            if (isEmpty())
                throw new RuntimeException("队列为空,无法出队");
            //先把要出队的数据用变量保存起来,因为front要变
            int data = arr[front];
            //注意front不能是简单的++,要考虑超出数组范围的情况,最终效果还是取到front的下一位(是在一个循环里)
            front = (front + 1) % MaxSize;
            System.out.println("已出队");
            return data;
    
        }
    
        //显示队列头的元素
        public int headQueue(){
            //先判断队列是否为空
            if (isEmpty()){
                throw new RuntimeException("队列空的, 没有数据");
            }
            return arr[front];
    
        }
    }
    //测试用数组实现的循环队列
    public class CircleArrayQueueDemo {
    
        public static void main(String[] args) {
            CircleArrayQueue arrayQueue = new CircleArrayQueue(5);//说明: 设置为5, 其队列的有效数据最大值为4
            boolean flag= true;
            Scanner sc = new Scanner(System.in);
            char key = ' ';//接收用户输入
            while (flag){
                //输出一个菜单
                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 = sc.next().charAt(0);//接收一个字符
                switch (key){
                    case 's':
                        arrayQueue.showQueue();
                        break;
                    case 'a':
                        System.out.println("请输入要入队的数据");
                        try {
                            int value = sc.nextInt();
                            arrayQueue.add(value);
                        }catch (Exception e){
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'g':
                        try {
                            int d = arrayQueue.getQueue();
                            System.out.println("取出的数据是: "+d);
                        }catch (Exception e){
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'h':
                        try {
                            int h = arrayQueue.headQueue();
                            System.out.println("队列头部的数据是: "+h);
                        }catch (Exception e){
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'e':
                        sc.close();
                        flag = false;
                        break;
                    default:
                        break;
                }
                System.out.println("======================");
            }
        }
    
    }
    
  3. 对取模原理的个人理解

    1. 假定给了一串数: 1 2 3 4 5 6 (注: 后面%6中的6 代表的意义是这串数字的个数 ,

      ​ 所以说如果是0~5,依然是 % 6)

    2. 若 小于6的数x对6取模, 即x % 6, 结果依然是x。(即便是把1~6 改为 0~5依然成立)

      若 大于6的数对6取模, 即x % 6, 结果就是比6大几就从前向后取几位

      (e.g 7 % 6 = 1, 8 % 6 = 2)(即便是把1~6 改为 0~5依然成立)

      这就对应了显示队列的所有数据的方法中取对应位置的值(i % MaxSize)

    3. 若 小于6的数x在 x加1 后对6取模, 即(x + 1) % 6, 结果依然是(x + 1),也就是紧挨着的下一位数。

      (即便是把1~6 改为 0~5依然成立)

      若 大于等于6的数x在 x加1 后对6取模, 即(x + 1) % 6, 结果就是比6大几就从前向后取几位

      (e.g 7 % 6 = 1, 8 % 6 = 2)(即便是把1~6 改为 0~5依然成立)

      这就对应了判断队列是否满了中的 (rear + 1) % MaxSize == front (若rear在最后一个位置,则(rear + 1)= 0,比6大 1, 从0开始取一位就是0, 即front所在位置)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值