队列(Queue)——先进先出(FIFO)的数据结构(Data Structures)

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

----------摘自百度百科

在日常生活中,排队是司空见惯的。我们去食堂打饭需要排队,买票需要排队,上下飞机也需要排队.

其实,生活中到处都是队列形式,从抽象层面上看,凡是两头开口的容器或通道都可以看做是队列,如水管、油管、电缆、隧道、单行车道等。

虽然队列是极为常见的,但要将队列现象进行抽象,才能将其提升为一种放之四海而皆准的队列数据结构。

一个队列有两个端:一个前段一个末端。所有的插入操作在队列的一端进行,所有的删除操作在队列的另一端进行,而进入队列的元素遵守先进先出的次序,即FIFO(First-In,First-Out)。这与栈元素的进出规则刚好相反,将先进先出的现象进行归纳就获得队列的非正式定义如下。

队列是一个满足如下条件的数据结构:

(1)数据项一个挨着一个呈线性;

(2)所有的插入操作在一端进行(对头);

(3)所有删除操作在另一端进行(对尾);

(4)最先插入的记录是第一个被删除的记录;

(5)队列的根本操作有三个:

①入队,在队尾加入一个元素或记录;

②出队,将队头元素或记录移除;

③读队,取出队头元素或记录。

因此,与栈一样,从本质上说,队列也是一种数据访问方式。

正是从这个角度出发,可以给队列下一个精确的定义如下。

队列结构是一个 某类型数据项组成的有穷序列,它支持一下操作。

(1)创建一个空队列;

(2)检测一个队列是否为空;

(3)如果队列未满,可以将一个元素加入到队列末尾;

(4)如果队列未空,可以将队列头元素删除;

(5)如果队列未空,可以将队列头元素取出;

(6)找出队列里缪安德元素个数;

(7)取出并删除队列头元素;

(8)清除队列里的素有元素。


下面是队列的C++定义。

//Queue in C++

typedef int queueEntry;
const int success = 0;
const int overflow = 1;
const int underflow = 2;

const int maxqueue = 100;										//队列的最大尺寸

class Queue 
{
public:
	Queue::Queue()												//队列构建函数,建立一个空队列
	{
		tail = 0;
		head = 0;
	}

	int Queue::serve()											//出队队首元素
	{
		if (tail <= 0)	
			return underflow;									//空队
		head++;
		return success;
	}

	bool Queue::empty() const									//判断队列是否为空
	{
		return head == tail;
	}

	int Queue::append(const queueEntry &item)					//将元素item插入队列末尾
	{
		if (tail >= maxqueue)
			return overflow;
		data[tail++] = item;
		return success;
	}

	int Queue::retrieve(queueEntry &item) const					//取出队首元素
	{
		if (tail <= 0)
			return underflow;
		item = data[head];
		return success;
	}
protected:
	int head, tail;
	queueEntry data[maxqueue];
};

细心的同学可能已经发现,上面给出的队列实现存在一个问题:每次出队一个元素,将头索引head往后移动一位(增加1)。每次入队一个元素,则将队尾索引tail往后移动一位。这样久而久之,就会在数组的前面出现一片空位。但尾索引到达数组的最右端(tail = maxqueue)时,就无法往队里增加元素了,此时数组前端却可能有空位。而这显然是我们不能(至少是不想)接受的。

解决的办法就是要利用数组前段的空位。最直接的办法是将所有的数据项前移。在这种实现方式下,头位置永远不变。每次出队一个元素,将后面的元素往前挪动一个位置。但末尾的索引这随着队列的变化而变化。

这种办法虽然可以解决空间浪费,但却因为需要移动大量数据而效率低下。更为高效的解决办法是,在尾索引达到数组最右端后回到数组的最左端,即在逻辑上将整个数组的空间看做是一个环形整体,这样就可以有效解决空间浪费问题。

由上所述,我们可以增加一个记数变量用来统计队列里的元素个数。这样,判断满或者空队列的条件就从尾位置是否相重改为判断该计数是否等于n。

所以,基于计数器的循环队列实现需要一个额外变量来记录当前队列元素个数,循环使用数组空间的能力则是通过队成员函数的修改而实现。因此需要修改的地方有两处:一是队列类的定义;二是成员函数的实现。下面是修改后的队列类定义。

//Queue in C++

typedef int queueEntry;
const int success = 0;
const int overflow = 1;
const int underflow = 2;
const int failure = -1;

const int maxqueue = 100;										//队列的最大尺寸

class Queue
{
public:
	Queue::Queue()												//队列构建函数,建立一个空队列
	{
		count = 0;
		tail = 0;
		head = 0;
	}

	int Queue::serve()											//出队队首元素
	{
		if (count <= 0)
			return underflow;									//空队
		count--;
		head = ((head + 1) == maxqueue) ? 0 : (head + 1);
		return success;
	}

	bool Queue::empty() const									//判断队列是否为空
	{
		return count == 0;
	}

	int Queue::append(const queueEntry &item)					//将元素item插入队列末尾
	{
		if (count >= maxqueue)
			return overflow;
		count++;
		data[tail] = item;
		tail = ((tail + 1) == maxqueue) ? 0 : (tail + 1);
		return success;
	}

	int Queue::retrieve(queueEntry &item) const					//取出队首元素
	{
		if (count <= 0)
			return underflow;
		item = data[head];
		return success;
	}
protected:
	int head, tail;
	int count;
	queueEntry data[maxqueue];
};

你可能感兴趣的:(数据结构)