数组模拟环形队列的学习心得
一、相关概念
> #### 队列
队列是一种常见的数据结构之一,最典型的、经常使用队列这种数据结构进行开发的,是叫号系统。
队列有以下几种特点:
- 先进先出:越早被存进队列的数据,就会越早被取出来;
- 属于线性结构,线性数据结构主要包括:数组、队列、链表以及栈;
- 队列可以通过数组和链表来表示。
二、数组模拟队列
> #### 数组模拟普通队列
![](https://img2020.cnblogs.com/blog/2409206/202105/2409206-20210526212818620-1123556200.png)
-
首先介绍一下图片中那些单词的具体含义:
① MaxSize: 代表着该队列的最大容量,也是数组的长度
② Queue: 是队列这个单词的英文写法
③ front: 指的是队列数据(图中红色部分)中的第一个数据的数组索引值再减去1
④ rear: 指的是队列数据(图中红色部分)中的最后一个数据的数组索引值
-
之所以会引用front以及rear这两个变量,是因为队列中数据的输入和输入是从前后端来处理的,需要这两个值来记录该队列的前后端在数组中的位置。其中,front会随着输出数据改变,rear会随着输入数据改变。
-
很容易可以推算出:如果 front == rear ,那么代表着队列中没有数据;如果 rear == maxSize -1 ,代表着队列已经存放满了数据。
-
代码实现:
//使用数组来模拟队列 class ArrayQueue{ private int maxSize; //表示数组的最大容量 private int front; //队列头 private int rear; //队列尾 private int[] arr; //用于模拟队列 //创建队列构造器 public ArrayQueue(int arrMaxSize) { this.maxSize = arrMaxSize; this.front = -1; //指向队列的头部,front指的的是队列头的前一个位置 this.rear = -1; //指向队列尾,指向队列尾的数据(即就是队列的最后一个数据) this.arr = new int[maxSize]; } //判断队列是否是满的 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++; //让rear后移 arr[rear] = n; } //从队列中获取数据 public int getQueue(){ //判断队列是否为空 if (isEmpty()){ //抛出异常 throw new RuntimeException("队列是空的,没有数据~~"); } 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]; } }
-
缺点:rear的值理论上不可超过数组的最大索引值,导致该数组只能存储一遍数据。优化的方式是把普通队列的形式改成环形队列的形式。
> #### 数组模拟环形队列
-
head代表着队列的第一个数据的索引值,tail代表着队列的的最后一个数据的后一个索引值
-
环形数组实现时,重点需要理解取模的含义。通过取模,就避免了tail索引值的越界,也就避免了数组不能重复使用的缺点
-
几个变量的值:
① tail == head : 代表队列为空
② (tail + 1) % maxSize == head: 代表队列已满
③ (tail + maxSize - head) % maxSize : 代表队列中的数据个数
-
代码实现
class CircleArray{ private int maxSize; //表示数组的最大容量 private int head; //队列头:指向的是队列的第一个数据,初始值为0 private int tail; //队列尾:指向的是队列的最后一个数据的后一个位置,初始值也是为0 private int[] arr; //用于模拟队列 public CircleArray(int arrMaxSize){ this.maxSize = arrMaxSize; this.arr = new int[maxSize]; } //判断队列是否是满的 public boolean isFull(){ return (tail + 1) % maxSize == head; } //判断队列是否为空 public boolean isEmpty(){ return tail == head; } //添加数据到队列 public void addQueue(int n){ //判断队列是否已满 if(isFull()){ System.out.println("队列已满,不能再次加入数据~~"); return; } //直接将数据加入 arr[tail] = n; //将tail后移,这里必须考虑取模 tail = (tail + 1) % maxSize; } //从队列中获取数据 public int getQueue(){ //判断队列是否为空 if (isEmpty()){ //抛出异常 throw new RuntimeException("队列是空的,没有数据~~"); } // 这里需要分析出head是指向队列的第一个元素 //1.先把head对应的值保存到一个临时的变量中去 //2.将head后移,这里需要取模,否则数组下标可能越界 //3.将临时变量返回 int value = arr[head]; head = (head + 1) % maxSize; return value; } //显示队列的所有数据 public void showQueue(){ if (isEmpty()){ System.out.println("队列是空的,没有数据~~"); return; } //思路:从head开始遍历,遍历多少个元素 for (int i = head; i < head + size(); i++) { System.out.printf("arr[%d]=%d\n",i % maxSize,arr[i % maxSize]); } } //求出当前队列有效数据的个数 public int size(){ return (tail + maxSize - head) % maxSize; } //显示队列的头数据,注意不是取数据 public int headQueue(){ //判断 if (isEmpty()){ throw new RuntimeException("队列是空的,没有数据~~"); } return arr[head]; } }