环形队列和映射解释
笔者正在学习数据结构与算法,本博客谨记录自己对环形队列的思考与解释。希望能够帮助大家对环形队列的实现有更深的认识。欢迎大家交流讨论,若有错误欢迎大家指正!
此博客探讨为什么环形队列的实现需要用到”取模“,揭示取模与映射的内在联系
队列ADT(抽象数据类型):
指向队头的front
指向队尾的rear
指定队列的最大容量
用于储存队列元素的数组
isFull方法:判断队列是否已满
isEmpty方法:判断队列是否为空
add方法:入列
get方法:出列
其中,入列出列遵循先进先出的原则
实现ADT
实现队列我们需要解决队列的每个元素怎么与数组的每个位置建立起映射的关系
方法的实现都基于这4个设定
设定1:front和rear都初始化为零。即front指向队首元素,rear指向队尾的后一个元素。
设定2:front和rear的取值范围都为{0,1,2,3,4,5,6,········无穷}
设定3(思考的场景):将front和rear想象成在数轴{0,1,2,3,4,5,6,········无穷}上的点
设定4:每次add,rear+1。每次get,front+1
- 为什么这么设定,这都是非常朴实无华的想法。比如我们要add,那么队列元素个数增加,rear需要+1。比如我们要get,队列元素个数减少,front需要+1。此时我们用rear-front来表示队列元素的个数就非常自然。
- (rear-front)在数轴上的含义:线段的长度。所以队列元素的个数可以用rear-front来表示
- 因为front和rear都会往前移动。所以看起来队列就像是移动的队伍。每个人可以按照front~(rear-1)进行编号。这里为什么是rear-1,可以想一想平时报数,从2报数到11,总人数是11-2+1。
- front到(real-1)共有rear-front个数。如何与数组建立映射关系呢?
- 注意到front与(real-1)很可能是经过很多次的add与get,所以值已经超过了MAX_SIZE。此时需要将front到(rear-1)的数取模,%MAX_SIZE之后front到(rear-1)之间的数就变成了0到(MAX_SIZE-1)的数。
- 所以映射关系是这样的。队列的元素与front到(rear-1)区间的点一一对应,front到(rear-1)上的点通过取模又与数组上的位置一一对应。最终形成了队列元素与数组位置的映射关系
但看上面的解释可能很抽象,可以根据下面的代码实现和批注帮助理解
Java代码实现
package Com.Hsp.DataStructure.Queue;
import java.util.Scanner;
public class ArrayQueueDemo2 {
public static void main(String[] args) {
Queue2 bankQueue = new Queue2(5);
boolean loop=true;
Scanner scanner = new Scanner(System.in);
char c=' ';
while(loop){
System.out.printf("输入a:入列\t");
System.out.printf("输入g:出列\t");
System.out.printf("输入s:展示队列\t");
System.out.printf("输入h:展示头元素\t");
System.out.printf("输入e:退出程序\n");
c=scanner.next().charAt(0);
switch (c){
case 'a':
System.out.println("输入一个整数");
int member=scanner.nextInt();
bankQueue.add(member);
break;
case 'g':
bankQueue.getMember();
break;
case 's':
bankQueue.showQueue();
break;
case 'h':
bankQueue.showHead();
break;
case 'e':
loop=false;
break;
default:
return;
}
}
System.out.println("退出成功~~~~");
}
}
/**
* 对队列1.0版本进行优化。将其优化成环形队列
*/
class Queue2{
private int front=0;//指向队列的头元素。将其看成数轴上的点,且取值范围为{1,2,3,4,5·······}
private int rear=0;//指向队列尾元素的前一个。将其看成数轴上的点,且取值范围为{1,2,3,4,5·······}
//注意:1. 队列元素与[front,rear-1]区间上的点一一对应(上面共有(rear-1)-(front)+1个队列元素)。
//2. 队列的元素共有(rear-1)-(front)+1=rear-front个。因为rear-front代表区间长度。所以队列的元素个数我们就用区间长度来代替
//3. 队列元素与[front,rear-1]区间上的点一一对应,[front,rear-1]区间上的点通过取模与arr[0]~arr[rear-1]的数组元素一一对应
// 至此形成了队列元素与数组元素一一对应的关系。————解释取模~~~
private int MAX_SIZE=0;//指定数组的最大容量
private int[] arr=null;
//到此队列的成员变量已经定义完毕
//构造器方法
public Queue2(int MAX_SIZE) {//初始化数据:还有MAX_SIZE和arr需要初始化
this.MAX_SIZE = MAX_SIZE;
arr=new int[MAX_SIZE];
}
//判断队列是否已满。在add之前要先判断是否队列已满
public boolean isFull(){
//判断队列满没满。思想:利用线段的长度,rear-front的长度代表有几个人.
return (rear-front)==MAX_SIZE;
}
//add方法,判断队列不为满才能往里面添加
public void add(int member){
if (isFull()){
return;
}
rear++;//每次添加是,rear后移,然后往rear之前的位置添加新元素
//队列元素与[front,rear-1]区间上的点一一对应,[front,rear-1]区间上的点通过取模与arr[0]~arr[rear-1]的数组元素一一对应
arr[((rear-1)%MAX_SIZE)]=member;
}
//判断队列是否为空。如果为空就不能取出
public boolean isEmpty(){
return rear==front;
}
//如果不为空。可以取出,且只能取出头部的元素,遵循先进先出的原则。
public int getMember(){
return arr[((front++)%MAX_SIZE)];//get(元素出列)之后front要后移一位
}
//method:显示队列。前提是队列不为空
public void showQueue(){
if (isEmpty()){
return;
}
int count=0;
for (int i = front; i <=rear-1 ; i++) {
count++;
System.out.printf("元素%d=%d\t",count,arr[i%MAX_SIZE]);
}
System.out.println();//打印完换行
}
//m:显示队列头部。前提是不为空
public void showHead(){
if (isEmpty()){
return;
}
System.out.println(arr[front]);
}
}