数据结构与算法分析(c++版) #10 顺序队列

本文探讨了如何高效地实现顺序队列,通过允许队列在数组中循环移动,使得enqueue和dequeue操作的时间代价均为θ(1)。然而,这种实现方式带来了如何判断队列满和空的问题,通过设置数组大小为n + 1并只存储n个元素,可以解决这一问题。
摘要由CSDN通过智能技术生成

顺序队列

       有效地实现顺序队列有些棘手,因为如果只是对顺序表的实现进行简单转换,效率不会很高。

       假设队列有n个元素,实现顺序表需要把所有元素都存储在数组的前n个位置上。如果选择队列的尾部元素放在位置0,则dequeue操作的时间代价仅为θ(1),因为队列最前面的一个元素(要被删除的元素)是数组最后面的一个元素(处于位置n - 1)。但是enqueue操作的时间代价为θ(n),因为必须把队列中当前n个元素的在数组中向后移动一个位置。如果反过来,把队列的尾部元素放在位置n - 1,则enqueue操作相当于线性表的append操作,时间代价仅为θ(1),但此时的dequeue操作的时间代价就为θ(n)了。

       如果放宽队列的所有元素必须出于数组的前n个位置这一条件,就可以得到一种更有效的实现方法。仍然保证队列的元素存储在连续的数组位置中,但是队列的内容允许在数组中移动,如下图所示。此时enqueue操作和dequeue操作的时间代价均为θ(1),因为队列中没有任何需要移动的元素。

                                    

                                          经过多次使用后,顺序队列的元素将移动到数组的后面。上图为一开始插入初始四个

                                                   数20,5,12和17后的队列;下图为先删除了20和5,然后插入3和4的队列

       但是这种实现方法有一个新的问题。假设初始时队列的头在位置0,新元素连续插入到数组中编号较高的位置上。当从队列中删除元素时,front的值增加。随着时间的推移,整个队列向数组中标号较高的位置移去。尽管此时数组的低端还可能有空闲的位置,即先前从队列中删除的元素所占的位置,一旦一个元素插入到数组中编号最高的位置上之后,队列的空间就用尽了。

       为了解决上述问题,我们可以通过假定数组是循环的来解决,即允许队列直接从数组中编号最高的位置延续到编号最低的位置。使用取模操作可以很容易实现。在这种方法中,数组中位置的编号为0到size - 1, size - 1被定义为位置0(等价于位置size % size) 的前趋。下图说明了这种方法。

                                                

       对于这种实现方式还有一个虽然小却十分重要的问题,就是如何判断队列是空还是满。因为根据上图可知,假设front存储队列中队首元素的位置号,rear存储队尾元素的位置号。当队列为满时front在rear的后面,当队列为空时front也在rear的后面,因为当队列中只有一个元素时,front和rear指向同一个元素。为了解决这个问题,我们可以设置数组的大小为n + 1,但是只存储n个元素,这样的话当队列为满时,rear的后一个位置是空数组空间,再后一个位置是front;队列为空时,rear的后一个位置就是front。

       顺序队列的实现如下:

template <class Elem> 
class AQueue : public Queue<Elem>
{
private:
	int size;	// 当前队列的大小
	int front;	// 队首的位置
	int rear;	// 队尾的位置;
	Elem *listArray;	// 用来存储队列中的元素的数组
	enum { DefaultListSize = 50 };	// 默认的队列最大范围
public:
	AQueue(int sz = DefaultListSize)
	{
		size = sz + 1;	// 创建的数组大小比需求的多一个
		rear = 0;
		front = 1;
		listArray = new Elem[size];
	}
	~AQueue() { delete[] listArray; }
//	清空队列的元素
	void clear() { front = 1; rear = 0; }
//	入队
	bool enqueue(const Elem& elem)
	{
		// 判断队列是否已满,rear后面的第二个元素是否为front
		if (((rear + 2) % size) == front) return false;
		rear = (rear + 1) % size;
		listArray[rear] = elem;
		return true;
	}
//  出队
	bool dequeue(Elem& elem)
	{
		//判断对视是否为空
		if (length() == 0) return false;
		elem = listArray[front];
		front = (front + 1) % size;
		return true;
	}
//  返回队首元素
	bool frontValue(Elem& elem) const
	{
		//判断对视是否为空
		if (length() == 0) return false;
		elem = listArray[front];
		return true;
	}
//  返回队列长度
	int length() const
	{
		return ((rear + size)- front + 1) % size;
	}
};






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值