循环队列

来源:互联网 发布:张继科mac口红什么梗 编辑:程序博客网 时间:2024/06/01 10:15

定义

队列的用处很广,最基本的有进程调度时的先来先服务、页面调度用到的先进先出,这里不讨论这种“FIFO”方式的优缺点,只说队列。队列属于数组概念的一个变种,在处理元素时包含两个属性,头标志和尾标志,对于入队和出队的操作通过移动头、尾来完成。与数组的比较如下


很明显,数组的插入与删除元素操作都在一端进行,而队列的插入元素在尾部进行,删除操作在头部进行。这就存在一个很明显的缺点:

相对于数组的重复利用数组空间而言,队列的空间利用率太低,虽然提供了先进先出的概念作为补偿,但是空间不能重复利用带来的损失太大。

由此诞生了循环队列:

队列的头、尾标志不变,只是在到达申请的数组空间的端点后,从数组开始处继续进行元素的插入、删除。逻辑上可以理解为在一个数组空间上,进行循环操作。如图


如果一来,可以在同一个空间内进行无数次的元素入队、出队操作。

结构

设数组空间大小为:length,头下标:front,尾下标:rear,初始状态:front=rear,即当前下标位置元素都为空

入队操作:

rear=(rear+1)%length;

正常数组中执行:rear++ 即可,此处因为可能到达尾部后从头开始,所以加操作后对数组长度取余数。当下标在数组长度内,则跟正常数组无异,当下标到达数组长度(即越界,因为正常数组最后一个下标是length-1),则从零开始,因此加上个取余操作。

出队操作:

由入队操作可知,出队操作类似:front=(front+1)%length;

根据入队和出队的操作可以看出,尾部rear始终指向一个空的位置,即下一个待入队元素的存储位置。front指向下一个待出队的元素位置(空队时除外)。由此可得出队列为空的条件:当front指向的位置(即待出队的元素位置)为rear指向的位置(即待入队元素的位置),此时队列为空(没有待出队元素)。

此时关于队列为满的判断,则可能需要牺牲掉数组空间的一个位置,因为按照正常“满”的概念,此时rear指向的下一个元素的插入位置,应该等于front指向的位置,即下一个准备入队的元素的位置空间上已经存储了元素,则表示队列“满”了,然而如果这样的话,则队列为满与队列为空的判断条件相同。所以为了避免该混乱情形,在判断队列满时,判断(rear+1)%length 的值是否与front相等,相等则表示队列满,即rear当前指向的空位置不存储元素,整个队列浪费一个存储空间,如果队列长度为1,即length=1时,则队列既为空,也为满,不能入队也不能出队。


(其实除了牺牲rear指向的元素存储空间来表示队列满外,还可以在队列中添加一个表示当前元素个数的属性,以此来判断队列为空为满的情形)

参考代码

class queue{  //循环队列    private int[] arr=null;//数组空间      private int front=0;//头下标      private int rear=0;//尾下标      public queue(int length){          arr=new int[length];       }  public queue(){arr=new int[10];//默认长度为10}    public void push(int s){//入队 if(full()){extend();//队列满则进行扩展}        arr[rear]=s;          rear=(rear+1)%arr.length;      }      public int pop(){//出队          if(!empty()){              int tem=arr[front];              front=(front+1)%arr.length;              return tem;          }          return -1;      }      public boolean full(){//判断满          return front==(rear+1)%arr.length;      }      public boolean empty(){//判断空          return front==rear;      }  public boolean In(int index){int i=front,j=rear;while(i!=j){if(arr[i]==index){return true;//i在队列中}i=(i+1)%arr.length;  }return false;//i不在队列中}private void extend(){int index=0;int new_len=arr.length*2;int[] new_arr=new int[new_len];while(front!=rear){new_arr[index++]=arr[front];front=(front+1)%arr.length;  }front=0;rear=index;arr=new_arr;}}  

总结

循环队列的使用较为广泛,主要优点有两个:一是相较于普通数组,提供了先进先出的概念,在使用功能上进行了拓展,二是在普通队列的元素操作方式上进行了改进,增加了空间的使用效率,虽然仍然浪费了一个存储单位的空间,但是相比之前已经优化了太多。



0 0
原创粉丝点击