循环队列

定义

队列的用处很广,最基本的有进程调度时的先来先服务、页面调度用到的先进先出,这里不讨论这种“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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值