队列

一、队列的定义和运算


1、队列的定义

       队列简称队,它也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。把进行插入的一端称作队尾,进行删除的一端称作队首。向队列中插入新元素称为进队或入队,新元素进队后就成为新的队尾元素;从队列中删除元素称为离队或出队,元素离队后,其后继元素就成为队首元素。由于队列的插入和删除操作分别是在各自的一端进行,每个元素必然按照进入的先后次序离队,所以又把队列称为先进先出表(FIFO表)。

2、队列接口的定义

    

public interface Queue {

	//抽象队列接口的定义
	void enter(Object obj);   //元素入队,即向队列尾部添加一个新元素obj
	Object leave();           //元素出队,即从队列首部删除队首元素并返回
	Object peek();            //返回队首元素的值
	boolean isEmpty();        //判断队列是否为空
	void clear();             //清除队列中的所有元素使之变为一个空队
}


二、队列的顺序存储结构和操作实现


       每次向队列插入一个元素,需要首先使队尾指针后移一个位置,然后再向这个位置写入新元素。当队尾指针指向数组空间的最后一个位置(即queueArray.length-1)时,若队首元素的前面仍存在空闲的位置,则表明队列未占满整个数组空间,下一个存储位置应是下标为0的空闲位置,因此,首先要使队尾指针指向下标为0的位置,然后再向该位置写入新元素。通过赋值表达式rear=(rear+1)%queueArray.length可使存储队列的整个数组空间变为队尾相接的一个环,所以顺序存储的队列又称为循环队列。在循环队列中,其存储空间是首尾循环利用的,当rear指向最后一个存储位置时,下一个所求的位置自动为数组空间的开始位置(即下标为0的位置)。

       每次从队列中删除一个元素时,若队列非空,则首先把队首指针后移,使之指向队首元素,然后再返回该元素的值。使队首指针后移也必须采用取模运算,该计算表达式为front=(front+1)%queueArray.length,这样才能够实现存储空间的首尾相接,即循环利用。

       当一个顺序队列中的长度域len域的值为0时,表明该队列为空,则不能进行出队和读取队首元素的操作,当len域的值等于queueArray.length时,表明队列已满,即存储空间已被用完,此时应动态扩大存储空间,接着才能插入新元素。

        在顺序存储的队列类型的定义中,若省略长度域len也是可行的,但此时的队列长度只能为queueArray.length-1,也就是说必须有一个位置空闲着。这是因为若使用全部queueArray.length个位置存储队列,则当队首和队尾指针指向同一个位置时,也可能为空队,也可能为满队,就存在二义性,无法判断。为了解决这个矛盾,只有牺牲一个位置的存储空间,利用队首和队尾指针是否相等作为判断空队的条件,而利用队尾指针加1并为queueArray.length取模后是否等于队首指针(即队尾是否从后面又追上了队首)作为判断满队的条件。

        对于队列的插入和删除操作分别在两端进行,并且通过使用循环队列后,不需要比较和移动任何元素,所以其时间复杂度均为O(1)。

       类的具体定义和每个方法的定义如下:

1、向队列插入元素

      当向队列插入一个元素时,是把它写入到当前队尾元素的后面,为此,要首先让队尾指针循环后移一个位置,若队列已满,则需要重新分配更大的数组存储空间,通常是原数组空间的二倍,此时还需要把原数组的内容复制到新数组空间中,然后才能正常插入元素。

//元素入队,即向队列尾部添加一个新元素obj
	public void enter(Object obj) {
		if((rear+1)%queueArray.length==front)       //队列满,重新分配空间  
		{
			Object [] p=new Object[queueArray.length*2];//扩大为二倍
			if(rear==queueArray.length-1)
			{
				//把原队列内容复制到新空间中的对应位置,此时front值为0
				for(int i=1;i<=rear;i++)
				{
					p[i]=queueArray[i];
				}
			}
			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;              //复制后修改首尾指针
			}
			queueArray=p;                              //使queueArray指向新队列的存储空间
		}
		rear=(rear+1)%queueArray.length;               //求出队尾的下一个循环位置
		queueArray[rear]=obj;                          //把obj的值赋给新的队尾位置
	}

各个方法的具体定义如下:

//顺序队列类的定义
public class SequenceQueue implements Queue {
	
	final int minSize=10;                           //假定存储队列的一维数组的初始长度为10
	private Object queueArray[];                    //定义存储队列的数组引用
	private int front ,rear;                        //定义队首和队尾指针
	
	//无参的构造方法的定义         
	public SequenceQueue() {  
		front=rear=0;                               //队列的初始为空,置队首和队尾指针值为0
		queueArray=new Object[minSize];             //数组初始长度为minSize的值10
	}
	
	//带初始长度参数的构造方法的定义
	public SequenceQueue(int n)                     
	{
		front=rear=0;                               //置队首和队尾指针值为0,初始队列为空
		if(n<=minSize)
		{
			n=minSize;                              //将n的值最小设置为minSize
			queueArray=new Object[n];               //给数组创建具有n大小的存储空间
		}
	}

	//元素入队,即向队列尾部添加一个新元素obj
	public void enter(Object obj) {
		if((rear+1)%queueArray.length==front)       //队列满,重新分配空间  
		{
			Object [] p=new Object[queueArray.length*2];//扩大为二倍
			if(rear==queueArray.length-1)
			{
				//把原队列内容复制到新空间中的对应位置,此时front值为0
				for(int i=1;i<=rear;i++)
				{
					p[i]=queueArray[i];
				}
			}
			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;              //复制后修改首尾指针
			}
			queueArray=p;                              //使queueArray指向新队列的存储空间
		}
		rear=(rear+1)%queueArray.length;               //求出队尾的下一个循环位置
		queueArray[rear]=obj;                          //把obj的值赋给新的队尾位置
	}

	//元素出队,即从队列首部删除队首元素并返回
	public Object leave() {
		if(front==rear)
		{
			return null;                               //若队列为空,则返回空值
		}
		front=(front+1) % queueArray.length;           //使队首指针指向下一个位置
		return queueArray[front];                      //返回队首元素的值
	}

	//返回队首元素的值
	public Object peek() {
		if(front==rear)
		{
			return null;                                //若队列为空则返回空值
		}
		return queueArray[(front+1) % queueArray.length];//返回队首元素
	}

	//判断队列是否为空
	public boolean isEmpty() {
		return front==rear;
	}

	//清除队列中的所有元素使之变为一个空队
	public void clear() {
		front=rear=0;
	}
}

三、队列的链接存储结构和实现

        队列的链接存储结构也是通过由结点构成的单链表实现的,此时只允许在单链表的表头进行删除和在单链表的表尾进行插入,因此它需要使用两个指针:队首指针front和队尾指针rear。用front指向队首(即表头)结点,用rear指向队尾(即表尾)结点。用于存储队列的单链表简称链接队列或链队。

        在链接存储的队列类中,其私有数据成员就是front和rear,而操作方法就是对Queue接口中定义的所有抽象方法的实现。假定链接队列类的名称用LinkQueue表示,该类的具体定义如下:

public class LinkQueue implements Queue{

	private Node front,rear;               //定义队首和队尾指针
	
	//无参构造函数的定义
	public LinkQueue() {
		front=rear=null;                   //初始队列为空,即使队首和队尾指针值为空
	}

	//向队列插入一个新元素obj,即插入到队列尾部,成为新的队尾
	public void enter(Object obj) {
		if(rear==null)                     //若链队为空,则新结点既是队首结点又是队尾结点
		{
			front=rear=new Node(obj,null);
		}
		else                               //若链队非空,则把新结点链接到队尾并修改队尾指针
			rear=rear.next=new Node(obj,null);
	}

	//元素出队,即从队列首部删除队首元素并返回
	public Object leave() {
		if(front==null)
		{
			return null;                    //对队列为空时的处理情况
		}
		Node x=front;                       //队首结点的引用暂存x
		front=front.next;                   //删除队首结点,相当于删除表头结点
		if(front==null)
		{
			rear=null;                      //若队列变为空,则队尾指针也必须变为空
		}
		return x.element;                   //返回原队首结点的值
	}

	 //返回队首元素的值
	public Object peek() {
		if(front==null)
		{
			return null;                    //对队列为空时的处理情况
		}
		return front.element;               //返回队首结点的值
	}

	//判断队列是否为空
	public boolean isEmpty() {
		return front==null;
	}

	//清除队列中的所有元素使之变为一个空队
	public void clear() {
		front=rear=null;
	}
}

  对于链接队列的插入和删除运算都不需要进行结点的比较和移动,只是简单地根据队尾指针插入结点和根据队首指针删除结点,所以其时间复杂度均为O(1),当然,其他在队列上进行运算的时间复杂度也为O(1)。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值