目录
- 数据结构之队列:链式存储结构与循环队列
- 链式存储结构
- 链式队列的结构
- 链式队列的操作
- 循环队列
- 循环队列的结构
- 循环队列的操作
- 链式队列与循环队列的比较
- 结论
- 3.2.3 队列的链式存储结构
- 3.2.3.1定义
- 3.2.3.2链式队列的基本操作——带头结点
- 3.2.3.3 不带头结点的相关操作
- 3.2.4双端队列
- 0. 限制输入输出问题(需要再验证)
- 1.定义
- 3.2.5循环队列
数据结构之队列:链式存储结构与循环队列
在计算机科学中,队列是一种常见的数据结构,它遵循先进先出(FIFO)的原则,即最先入队的元素将最先出队。队列广泛应用于计算机系统中,例如任务调度、缓冲区管理等。在队列的实现中,链式存储结构和循环队列是两种常见的方法。本文将对这两种队列的实现方法进行介绍和比较。
链式存储结构
链式存储结构是通过链表来实现队列的一种方法。队列中的每个元素被封装成一个节点,节点之间通过指针链接在一起。链式队列允许动态地添加和删除元素,不需要事先定义队列的大小,因此具有较好的灵活性。
链式队列的结构
一个链式队列节点通常包含两个部分:
- 数据域:用于存储节点所包含的数据。
- 指针域(next):指向队列中下一个节点的指针。
struct QueueNode {
int data;
QueueNode* next;
};
链式队列的操作
链式队列支持多种操作,包括元素的入队和出队等。由于队列的元素在链表中动态存储,因此链式队列的大小可以根据实际需求进行调整,不会浪费内存空间。
循环队列
循环队列是一种使用数组实现的队列。与链式队列相比,循环队列具有固定大小,一旦队列的大小被定义,就无法动态扩展。循环队列通过“头指针”和“尾指针”来实现循环存储,当队列满时,新元素将覆盖最先入队的元素,实现循环使用队列空间。
循环队列的结构
循环队列通常包含以下几个部分:
- 数据数组:用于存储队列中的元素。
- 头指针(front):指向队列头部的位置。
- 尾指针(rear):指向队列尾部的下一个位置。
#define MAX_SIZE 100
class CircularQueue {
private:
int data[MAX_SIZE];
int front;
int rear;
public:
CircularQueue();
bool isEmpty();
bool isFull();
void enqueue(int value);
int dequeue();
};
循环队列的操作
循环队列支持多种操作,包括元素的入队和出队等。由于队列是循环使用数组空间,因此入队和出队操作需要更新头指针和尾指针,并考虑数组下标的循环。
链式队列与循环队列的比较
链式队列和循环队列是两种不同的队列实现方式,它们各自具有优势和适用场景。
链式队列的优势:
- 动态大小:链式队列可以根据实际需求动态调整大小,不会浪费内存空间。
- 灵活性:链式队列允许动态添加和删除元素,操作更加灵活。
循环队列的优势:
- 固定大小:循环队列使用数组实现,具有固定大小,不会频繁申请和释放内存,效率较高。
- 循环利用:循环队列通过循环使用队列空间,可以有效地利用数组空间,减少内存碎片。
结论
链式存储结构和循环队列是两种常见的队列实现方法。在选择使用哪种队列实现时,需要根据具体的应用场景和操作需求来进行判断。无论是链式队列还是循环队列,队列数据结构在计算机科学中扮演着重要的角色,为我们提供了高效管理数据的方法。
3.2.3 队列的链式存储结构
3.2.3.1定义
1.定义:队列的链式表示称为链队列,它实际上是一个同时带有队头指针和队尾指针的单链表。
链队列:用链表表示的队列,是限制仅在表头删除和表尾插入的单链表。
队列的链式存储类型可描述为:
typedef struct LinkNode{ //链式队列结点
ElemType data;
struct LinkNode *next;
}
typedef struct{ //链式队列
LinkNode *front, *rear; //队列的队头和队尾指针
}LinkQueue;
3.2.3.2链式队列的基本操作——带头结点
- 初始化 & 判空
void InitQueue(LinkQueue &Q){
//初始化时,front、rear都指向头结点
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
Q.front -> next = NULL;
}
//判断队列是否为空
bool IsEmpty(LinkQueue Q){
if(Q.front == Q.rear) //也可用 Q.front -> next == NULL
return true;
else
return false;
}
- 入队操作
//新元素入队 (表尾进行)
void EnQueue(LinkQueue &Q, ElemType x){
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode)); //申请一个新结点
s->data = x;
s->next = NULL; //s作为最后一个结点,指针域指向NULL
Q.rear->next = s; //新结点插入到当前的rear之后
Q.rear = s; //表尾指针指向新的表尾
}
- 出队操作
//队头元素出队
bool DeQueue(LinkQueue &Q, ElemType &x){
if(Q.front == Q.rear)
return false; //空队
LinkNode *p = Q.front->next; //p指针指向即将删除的结点 (头结点所指向的结点)
x = p->data;
Q.front->next = p->next; //修改头结点的next指针
if(Q.rear == p) //此次是最后一个结点出队
Q.rear = Q.front; //修改rear指针
free(p); //释放结点空间
return true;
}
-
队列满的条件
顺序存储:预分配存储空间
链式存储:一般不会队满,除非内存不足 -
计算链队长度 (遍历链队)
设置一个int length 记录链式队列长度
3.2.3.3 不带头结点的相关操作
3.2.4双端队列
0. 限制输入输出问题(需要再验证)
对于双端队列的限制输出以及输出问题,做以下考虑
- 限制输入的,就意味着单边输入,注意输入顺序,怎么输入就怎么按照什么顺序写下去,再去左右判断是否可以输出
- 限制输出的,就意味着单边输出,注意输出顺序,按照输入顺序的第一个,选项中左右寻找临近的输入顺序,找得到则合法
1.定义
双端队列是指允许两端都可以进行入队和出队操作的队列
- 双端队列允许从两端插入、两端删除的线性表;
- 如果只使用其中一端的插入、删除操作,则等同于栈;
- 输入受限的双端队列:允许一端插入,两端删除的线性表;
- 输出受限的双端队列:允许两端插入,一端删除的线性表;
栈合法的序列,双端序列也合法(只用一边就是一个栈),所以只要考虑不合法的那些