• 【数据结构(二)】队列(2)


    1. 队列的应用场景和介绍

    1.1. 队列的一个使用场景

        
    银行排队的案例

    在这里插入图片描述

    1.2. 队列介绍

    1. 队列是一个有序列表,可以用数组或是链表来实现。
    2. 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出

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

    在这里插入图片描述

    2. 数组模拟队列

    2.1. 思路分析

    • 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。
    • 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 frontrear 分别记录队列前后端的下标,front
      会随着数据输出而改变,而 rear 则是随着数据输入而改变,

    如图所示:

    在这里插入图片描述

    当我们将数据存入队列时称为”addQueue”,addQueue 的处理需要有两个步骤:思路分析

        ①将尾指针往后移:rear+1 (当 front == rear 【队列为空】)

        ②若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。(rear == maxSize - 1[队列满])

    2.2. 代码实现

    package queue;
    
    import java.util.Scanner;
    
    public class ArrayQueueDemo {
        public static void main(String[] args) {
            //测试一把
            //创建一个队列
            ArrayQueue queue = new ArrayQueue(3);
            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':
                        queue.showQueue();
                        break;
                    case 'a':
                        System.out.println("输入一个数");
                        int value = scanner.nextInt();
                        queue.addQueue(value);
                        break;    
                    case 'g'://取出数据
                        try {
                            int res = queue.getQueue();
                            System.out.printf("取出的数据是%d\n", res);
                        } catch (Exception e) {
                            // TODO: handle exception
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'h'://查看队列头的数据
                        try {
                            int res = queue.headQueue();
                            System.out.printf("队列头的数据是%d\n", res);
                        } catch (Exception e) {
                            // TODO: handle exception
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'e'://退出
                        scanner.close();
                        loop = false;
                        break;
    
                
                    default:
                        break;
                }
                
            }
    
            System.out.println("程序退出~~");
    
        }
    }
    
    //使用数组模拟队列——编写一个ArrayQueue类
    class ArrayQueue{
        private int maxSize;//表示数组的最大容量
        private int front;//队列头
        private int rear;//队列尾
        private int[] arr;//该数据用于存放数据,模拟队列
    
        //创建队列的构造器
        public ArrayQueue(int arrMaxSize){
            maxSize = arrMaxSize;
            arr = new int[maxSize];
            front = -1;//指向队列头部,front是指向队列头的前一个位置
            rear = -1;//指向队列尾,指向队列尾的数据(即 就是队列最后一个数据)
        }
    
        //判断队列是否满
        public boolean isFull(){
            return rear == maxSize - 1;
        }
    
        //判断队列是否为空
        public boolean isEmpty(){
            return rear == front;
        }
    
        //添加数据到队列
        public void addQueue(int n){
            //判断队列是否满
            if(isFull()){
                System.out.println("队列满,不能加入数据~");
                return;
            }
            rear++;
            arr[rear] = n;
        }
    
        //获取队列列表,出队列
        public int getQueue(){
            //判断队列是否为空
            if(isEmpty()){
                //通过抛出异常
                throw new RuntimeException("队列空,不能取数据");
            }
            front++;//front后移
            return arr[front];
        }
    
        //显示队列的所有数据
        public void showQueue(){
            //遍历
            if(isEmpty()){
                System.out.println("队列空的,没有数据~~");
                return;
            }
            for(int i = 0; i < arr.length; i++){
                System.out.printf("arr[%d]=%d\n", i, arr[i]);
            }
        }
    
        //显示队列的头数据,注意不是取出数据
        public int headQueue(){
            //判断
            if(isEmpty()){
                throw new RuntimeException("队列空的,没有数据~");
            }
            return arr[front + 1];
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135

    运行结果:

    在这里插入图片描述

    注意:上述代码存在问题
    虽然已经把所有数据取出来了,但是不能继续往里面加数据了,演示如下
    在这里插入图片描述

    问题分析并优化:

    1. 目前数组使用一次就不能用, 没有达到复用的效果
    2. 将这个数组使用算法,改进成一个环形的队列 取模:%

    3. 数组模拟环形队列

    3.1. 思路分析

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

    分析说明:

    1. 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意 (rear + 1) % maxSize == front [队列满]
    2. rear == front [队列空]

    分析示意图:

    在这里插入图片描述

    思路如下:

    1. front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素。 front 的初始值 = 0
    2. rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定。rear 的初始值 = 0
    3. 当队列满时,条件是 (rear + 1) % maxSize == front
    4. 对队列为空的条件, rear == front
    5. 当我们这样分析, 队列中有效的数据的个数 为:(rear + maxSize - front) % maxSize 。比如 rear = 1,front = 0,有1个有效数据

    3.2. 代码实现

    package queue;
    
    import java.util.Scanner;
    
    public class CircleArrayQueueDemo {
    
    	public static void main(String[] args) {
    		
    		//测试一把
    		System.out.println("测试数组模拟环形队列的案例~~~");
    		
    		// 创建一个环形队列
    		CircleArray queue = new CircleArray(4); //说明设置4, 其队列的有效数据最大是3
    		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':
    				queue.showQueue();
    				break;
    			case 'a':
    				System.out.println("输出一个数");
    				int value = scanner.nextInt();
    				queue.addQueue(value);
    				break;
    			case 'g': // 取出数据
    				try {
    					int res = queue.getQueue();
    					System.out.printf("取出的数据是%d\n", res);
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case 'h': // 查看队列头的数据
    				try {
    					int res = queue.headQueue();
    					System.out.printf("队列头的数据是%d\n", res);
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case 'e': // 退出
    				scanner.close();
    				loop = false;
    				break;
    			default:
    				break;
    			}
    		}
    		System.out.println("程序退出~~");
    	}
    
    }
    
    
    class CircleArray {
    	private int maxSize; // 表示数组的最大容量
    	//front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素 
    	//front 的初始值 = 0
    	private int front; 
    	//rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.
    	//rear 的初始值 = 0
    	private int rear; // 队列尾
    	private int[] arr; // 该数据用于存放数据, 模拟队列
    	
    	public CircleArray(int arrMaxSize) {
    		maxSize = arrMaxSize;
    		arr = new int[maxSize];
    	}
    	
    	// 判断队列是否满
    	public boolean isFull() {
    		return (rear  + 1) % maxSize == front;
    	}
    	
    	// 判断队列是否为空
    	public boolean isEmpty() {
    		return rear == front;
    	}
    	
    	// 添加数据到队列
    	public void addQueue(int n) {
    		// 判断队列是否满
    		if (isFull()) {
    			System.out.println("队列满,不能加入数据~");
    			return;
    		}
    		//直接将数据加入
    		arr[rear] = n;
    		//将 rear 后移, 这里必须考虑取模
    		rear = (rear + 1) % maxSize;
    	}
    	
    	// 获取队列的数据, 出队列
    	public int getQueue() {
    		// 判断队列是否空
    		if (isEmpty()) {
    			// 通过抛出异常
    			throw new RuntimeException("队列空,不能取数据");
    		}
    		// 这里需要分析出 front是指向队列的第一个元素
    		// 1. 先把 front 对应的值保留到一个临时变量
    		// 2. 将 front 后移, 考虑取模
    		// 3. 将临时保存的变量返回
    		int value = arr[front];
    		front = (front + 1) % maxSize;
    		return value;
    
    	}
    	
    	// 显示队列的所有数据
    	public void showQueue() {
    		// 遍历
    		if (isEmpty()) {
    			System.out.println("队列空的,没有数据~~");
    			return;
    		}
    		// 思路:从front开始遍历,遍历多少个元素
    		// 动脑筋
    		for (int i = front; i < front + size() ; i++) {
    			System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
    		}
    	}
    	
    	// 求出当前队列有效数据的个数
    	public int size() {
    		// rear = 2
    		// front = 1
    		// maxSize = 3 
    		return (rear + maxSize - front) % maxSize;   
    	}
    	
    	// 显示队列的头数据, 注意不是取出数据
    	public int headQueue() {
    		// 判断
    		if (isEmpty()) {
    			throw new RuntimeException("队列空的,没有数据~~");
    		}
    		return arr[front];
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
  • 相关阅读:
    C++的指针简明教程
    Java连接Redis并操作Redis中的常见数据类型
    java计算机毕业设计高校图书馆管理网站源码+mysql数据库+系统+lw文档+部署
    混迹职场10多年的数据开发老鸟,居然被一个职场新人上了一课
    leetcode:207. 课程表
    数字塔问题
    实例042:变量作用域
    redis未授权访问漏洞
    MYSQL-入门
    《QT从基础到进阶·三十二》Q指针和D指针用法
  • 原文地址:https://blog.csdn.net/weixin_44883789/article/details/134470378