为什么使用循环队列?
因为队列是一种先进先出的存储结构,并且元素只可以在队尾插入,在队头删除。在这种多次的插入、删除操作后,就会出现一种“假溢出”现象。“假溢出”现象的意思就是在队列前面还有空余位置的时候,最后面已经到达了队尾,无法再插入新的元素了。
代码解析
首先是循环队列基本操作的代码(代码是教材是教材上的,只有一些细微的改变)
public class sequenceQueue<T> {
final int MaxSize=10;
private T queueArray[];
private int front,rear;
1 最开始是定义了一个名为sequenceQueue的类,其中 T 为泛类型。
2 设定了这个循环队列的初始长度(最大长度)MaxSize。
3 存储元素的队列数组对象。
4 队列的头指针和尾指针。
【队列的初始化】
public sequenceQueue(){
front=rear=0;
queueArray=(T[])new Object[MaxSize];
}
1 无参初始化
2 在之前有两个指针,头指针和尾指针,在初始时让头指针和尾指针都在“0”号位
3 由于不能实例化一个泛型对象,所以在构造时可以先实例化一个新的最大长度为MaxSize的Object数组。
【入队】
public void EnQueue(T obj){
if((rear+1)%queueArray.length==front){
T[] p=(T[])new Object[queueArray.length*2];
1 因为循环队列是一个圈圈型的,所以如果尾指针+1和头指针在一个位置,就说明这个循环队列已经满了。
2 在前面判断完循环队列是否已经满了。如果满了,就要申请新的空间(原空间的2倍)。
if(rear==((T[])queueArray).length-1){
for(int i=1;i<=rear;i++)
p[i]=queueArray[i];
}
在判断循环队列已经满了,申请了新的空间,就需要把原来空间中已存在的元素移到新的空间中,但在转移元素时,元素在原来的空间中是有两种排列情况的,上面这段代码就是其中的一种情况——头指针在尾指针前面,这个说法有点抽象,看我画的图图叭!
啊啊啊!对了!在图里可以看出0号位上没有任何元素,这是循环队列的一个特点!为了可以有效地区分队空和队满的情况,如果把front指针所指的位置空出来,这样就可以通过rear== front来确定队空,通过(rear+1) % queueArray.length==front;来判断队满。
那么问题又来了! (rear+1) % queueArray.length == front;这个是啥?就又回到了一个很基本的问题,循环队列是怎么实现的。循环队列最本质思想就是取余数,%就是其余的意思。
继续解释代码
1 判断rear的位置是否在最后面(就是循环队列中0号位前面的那个)
2-3 如果是,直接用for循环语句从1号位的元素,移到最后面rear指针指的元素。注意!在移动到新表时要从1号位开始放哦!
else{
int i,j=1;
for(i=front=1;i<queueArray.length;i++,j++)
p[j]=queueArray[i];
for(i=0;i<=rear;i++,j++)
p[j]=queueArray[i];
front=0;
rear=queueArray.length-1;
}
从else就能看出来这是另一种情况了!就是尾指针不在最后一个位置的时候,还是看图吧!
这种情况在移动的时候就有点麻烦了!不仅仅要移动过去,还要把这个顺序掰正了!
解释代码
1 首先要把新表的0号位让出来,原因和前面说的一样,为了区分满和空要牺牲一个空间。
2-5 就是把元素复制过去的代码,其中2-3是先复制头指针后面的一位(因为front指针指的位置为空)到最后一个位置的元素(图中就是BCDE元素),4-5行为复制0号位到尾指针所指的元素(图中为F)
6-7 再给头指针和尾指针指到新表的对应位置~
queueArray=p;
}
rear=(rear+1)%queueArray.length;
queueArray[rear]=obj;
}
1 给新表一个新的名字,申请的时候他叫p,元素都挪完了,名字也给他吧!
2-3 终于!!入队啦!先把尾指针指到下一个位置,再把元素放进去。
【出队】
public T DeQueue(){
if(isEmpty()){
System.out.println("队列已空,无法出队!");
return null;
}
front=(front+1)%queueArray.length;
return queueArray[front];
}
出队就没那么多乱七八糟的麻烦了!只需要判断队列是否为空就好~
代码解释
1-3 体现了一种容错能力,就是在出队时首先要判断队伍的情况,队伍里啥都没有就直接打印"队列已空,无法出队!"
4-5 队列是先进先出的结构,因此在出队时需要先把头指针后移,然后把其中的元素移除。
【取头元素】
public T GetTop(){
if(isEmpty()){
System.out.println("队列已空,无法读取元素!");
return null;
}
front=(front+1)%queueArray.length;
return queueArray[front];
}
1-3 和出队差不多,既然要取头元素,那就要先判断队列是否为空
4-5 取头元素,返回头指针后一个位置的元素,还有另一种写法,可以给这两句话写成一句,如下。
return queueArray[(front+1)%queueArray.length];
【判断队列是否为空】
public boolean isEmpty(){
return front==rear;
}
1-2 如果front和rear相等,就返回true,反之返回false。
【求队列长度】
public int size(){
return (rear-front+queueArray.length)%queueArray.length;
}
就像前面的第一张图,rear指针在5,front指针在0,图中的队列长度为6。
计算过程:(5-0+6)%6=5,就是五个元素吖!
第二张图,rear指针在0,front指针在1,图中队列长度为6.
计算过程:(0-1+6)%6=5,一共五个元素!
【遍历队列】
public void nextOrder(){
int i,j=front;
for(i=1;i<=size();i++){
j=(j+1)%queueArray.length;
System.out.println(queueArray[j]);
}
}
1 申请两个变量,i和j,其中j在front指针的位置。
2-4 for循环i是防止重复遍历,j是用来取元素输出元素。
【清空队列】
public void clear(){
front=rear=0;
}
}
头指针尾指针直接回到解放前,全部打回到0号位。
接下来是主类的代码部分(教材上只给了上面方法的代码,下面的主类代码,我也忘了当时是网上找的还是编的了,但是我会尽力解释的!!!)
public class Text5 {
public static void main(String[] args) {
sequenceQueue<Integer> sequenceQueue = new sequenceQueue<Integer>();
sequenceQueue.EnQueue(23);
sequenceQueue.EnQueue(56);
sequenceQueue.EnQueue(12);
sequenceQueue.EnQueue(49);
sequenceQueue.EnQueue(35);
1 申请新的int类型的数组
2-6 入队入队入队
Integer s = sequenceQueue.size();
System.out.println(sequenceQueue.GetTop());
1 定义一个int型变量s,存放队列长度
2 输出头指针
for (Integer integer = 0;integer<s;integer++){
System.out.println(sequenceQueue.DeQueue());
}
1-2 全部出队!
System.out.println(sequenceQueue.isEmpty());
判空,输出结果为true或false
sequenceQueue.EnQueue(35);
s=sequenceQueue.size();
1 入队
2 求队列长度
for(Integer integer = 0;integer<s;integer++){
System.out.println(sequenceQueue.DeQueue());
}
System.out.println(sequenceQueue.isEmpty());
}
}
1-2 又删???
3 又判空???
xswl当时我咋没注意到,这么可爱的操作…