您的位置:首页 > 理论基础 > 数据结构算法

数据结构学习笔记——队列

2016-01-17 16:58 288 查看

队列的操作特性

先进先出,后进后出。

队列类型的实现

链队列——链式映像

typedef struct QNode
{
QElemType data;
QNode *next;
}*QueuePtr;

struct LinkQueue
{
QueuePtr front,rear; // 队头、队尾指针
};


循环队列——顺序映像

用一组地址连续的存储单元依次存放从队头到队尾的元素。

附设两个指针front和rear分别指示队头元素的位置和队尾元素的下一个位置。

初始化建空队列时令front=rear=0,插入新的队尾元素时尾指针增加1,删除队头元素时头指针增加1。

普通的顺序队列会出现假上溢的情况。

因为在对队列的操作中,头尾指针只增加不减小,导致被删除元素的空间永远无法重新利用。尽管队列中实际的元素个数可能远远小于存储空间的规模,但仍不能做入队操作,该现象称为“假上溢”。

克服“假上溢”现象的方法是将顺序队列想象为一个首尾相接的圆环,称之为循环队列。

循环对列中无法通过Q.front=Q.rear来判断队列“空”还是“满”。解决此问题的方法至少有3种:

1. 另设一个标志位区别队列的“空”和“满”。

2. 使用一个计数器记录队列中元素的总数(也就是队列的长度)。

3. 少用一个元素的空间,约定以“队列头指针在队列尾指针的下一个位置(指环状的下一个位置)上”作为队列呈“满”状态的标志。

以下采用的是第3种方法。

循环队列的类型定义

#define MAXQSIZE 5 // 最大队列长度(对于循环队列,最大队列长度要减1)
struct SqQueue
{
QElemType *base; // 初始化的动态分配存储空间
int front; // 头指针,若队列不空,指向队列头元素
int rear; // 尾指针,若队列不空,指向队列尾元素的下一个位置
};


循环队列需要注意以下几点:

如果Q.front==Q.rear,则可以判断循环队列为空;

如果(Q.rear+1)%MAXQSIZE==Q.front,则可以判断循环队列为满。

无论是对循环队列进行插入或删除元素时,均可能涉及到尾指针或头指针的调整(非简单地对指针进行+1操作),即

Q.rear=(Q.rear+1)%MAXQSIZE或Q.front=(Q.front+1)%MAXQSIZE

如何理解(Q.rear-Q.front+MAXQSIZE)%MAXQSIZE即为循环队列的长度

当Q.rear>Q.front时,循环队列长度=Q.rear-Q.front;

当 Q.rear<Q.frontQ.rear时,循环队列长度=(Q.rear-Q.front+MAXQSIZE);

综合考虑以上两种情况,循环队列长度(任何情况下)=(Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;

队列与循环队列

关于循环队列是存储结构还是逻辑结构是有争议的,我更倾向于以下观点:

队列(包括循环队列)是一个逻辑概念,而链表是一个存储概念。一个队列是否是循环队列,不取决于它将采用何种存储结构,根据实际的需要,循环队列可以采用顺序存储结构,也可以采用链式存储结构,包括采用循环链表作为存储结构。

数据通过栈和队列产生的序列有什么不同?

一串数据依次通过一个栈,并不能保证出栈的次序总是倒置的,可以产生多种出栈序列。

一串数据通过一个栈后的次序由每个数据之间的入栈、出栈操作序列决定,只有当所有数据“全部入栈后再全部出栈”才能使数据倒置。事实上,存在一种操作序列——“入栈、出栈、入栈、出栈…”可以使数据通过栈后仍然保持次序不变。

一串数据通过一个队列,只有一种出队列顺序,就是其入队列顺序。

几种特殊的队列:

双端队列:可以在双端进行插入和删除操作的线性表。

输入受限的双端队列:线性表的两端都可以输出数据元素,但是只能在一端输入数据元素。

输出受限的双端队列:线性表的两端都可以输入数据元素,但是只能在一端输出数据元素。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: