👍数据结构——队列queue(c)-受限线性表
文章目录
一、队列定义
队列 是仅限定在表尾进行插入和表头进行删除操作的线性表。
特点 先进先出。(故又称为先进先出表FIFO-First In First Out)
相关定义:
①队尾 允许插入的一端(两种情况:(1)队尾元素;(2)队尾的下一位)
②队头 允许删除的一端
③空队列
④入队 EnQueue
⑤出队 DeQueue
二、队列的顺序存储结构——循环队列
1.基于队尾指针指向队尾元素的下一位
顺序队列不循环时存在什么问题?
答:从创建空的队列开始
Status InitQueue(SqQueue &Q){
Q.base = new QElemtype [MAXSIZE];
if(!Q.base) return overflow;
Q.front = Q.rear = 0;
return success;
}
易知:
判空的条件: Q.rear == Q.front = 0;
判满的条件:Q.rear == MAXSIZE;
队列不满时,新元素new_element入队:Q.base[Q.rear] = new_element; Q.rear++;
队列长度:Q.rear - Q.front
队头元素:Q.base[Q.front]
队尾元素:Q.base[Q.rear-1]
队列不空时,队头元素出队: Exit_element = Q.base[Q.front]; Q.front++;
易知,容易造成假溢出。
解决方法:将队列空间看成是逻辑上的环状空间!
如下:
(1)当Q.rear == MAXSIZE;
时,将其置为0;
(2)或当Q.rear == Q.rear % MAXSIZE;
但此时,队列空/队列满均为Q.rear == Q.front
解决方法:
- 在定义中设置一个队列长度计数器Q.length。
初始值为0(队列空)。
当一个元素入队列时+1,出队列时-1。
队列空:Q.length == MAXSIZE
队列满:Q.length == 0
队列长度:Q.length
- 令队列空间中的一个单元闲置,使得在任何时刻,保持Q.rear和Q.front之间至少隔一个空闲单元。
队列空:Q.rear == Q.front
队列满:Q.front == (Q.rear + 1) % MAXSIZE
队列长度:( Q.rear - Q.front + MAXSIZE ) % MAXSIZE
- 在定义中设置一个变量tag,每次删除操作成功时,tag=0;每次插入操作成功时,tag=1
队列空:Q.rear == Q.front && Q.tag == 0
队列满:Q.rear == Q.front && Q.tag == 1
队列长度:队列不满时:((Q.rear - Q.front + MAXSIZE)) % MAXSIZE
队列满时:MAXSIZE
具体实现
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdbool.h>
#define MAXSIZE 100
typedef struct SqQueue{
int *base;
int front;
int rear;
}SqQueue;
void InitQueue(SqQueue *Q); //初始化队列Q
bool QueueEmpty(SqQueue Q); //判断队列是否为空
void EnQueue(SqQueue *Q, int e); //入队
int DeQueue(SqQueue *Q); //出队
int GetFront(SqQueue *Q); //获取队头元素,但不从队列中移走该元素
int length(SqQueue Q); //计算并返回队列中元素的个数
//初始化队列Q
void InitQueue(SqQueue *Q){
Q->base = (int *)malloc(MAXSIZE * sizeof(int));
if(!Q->base){
printf("内存分配失败!");
return;
}
Q->front = Q->rear = 0;
}
//判断队列是否为空
bool QueueEmpty(SqQueue Q){
if(Q.front == Q.rear)
return true;
else
return false;
}
//入队
void EnQueue(SqQueue *Q, int e){
if((Q->rear+1)%MAXSIZE == Q->front){
printf("队列满!");
return;
}
Q->base[Q->rear] = e;
Q->rear = (Q->rear+1)%MAXSIZE;
}
//出队
int DeQueue(SqQueue *Q){
if(Q->front == Q->rear){
printf("队列空!");
return 0;
}
int e = Q->base[Q->front];
Q->front = (Q->front + 1)%MAXSIZE;
return e;
}
//获取队头元素,但不从队列中移走该元素
int GetFront(SqQueue *Q){
if(Q->front == Q->rear){
printf("队列空!");
return 0;
}
return Q->base[Q->front];
}
//计算并返回队列中元素的个数
int length(SqQueue Q){
return (Q.rear - Q.front + MAXSIZE)%MAXSIZE;
}
int main()
{
SqQueue Q;
InitQueue(&Q);
for(int i = 0; i < 10; i++){
EnQueue(&Q, i);
}
printf("%d ", GetFront(&Q));
printf("此时长度为%d\n", length(Q));
DeQueue(&Q);
printf("%d ", GetFront(&Q));
printf("此时长度为%d\n", length(Q));
return 0;
}
2.基于队尾指针指向队尾元素
为了使队列中的全部元素入队列,出队列等相关操作一致,可在初始化队列时,使front指向队头,rear指向队头的前一个位置。
- 在定义中设置一个队列长度计数器Q.length。
初始值为0(队列空)。
当一个元素入队列时+1,出队列时-1。
队列空:Q.length == MAXSIZE
队列满:Q.length == 0
队列长度:Q.length
- 令队列空间中的一个单元闲置,使得在任何时刻,保持Q.rear和Q.front之间至少隔一个空闲单元。
队列空:(Q.rear + 1) % MAXSIZE == Q.front
队列满:Q.front == (Q.rear + 2) % MAXSIZE
队列长度:( Q.rear + 1 - Q.front + MAXSIZE ) % MAXSIZE
- 在定义中设置一个变量tag,每次删除操作成功时,tag=0;每次插入操作成功时,tag=1
队列空:(Q.rear + 1) % MAXSIZE == Q.front && Q.tag == 0
队列满:(Q.rear + 1) % MAXSIZE == Q.front && Q.tag == 1
队列长度:队列不满时:((Q.rear + 1 - Q.front + MAXSIZE)) % MAXSIZE
队列满时:MAXSIZE
三、队列的链式存储实现——链队列
显然需要两个分别指向队头和队尾的指针才能惟一确定。
优点:一般来说不会队满,除非内存不足。
注:
①对于链式存储实现队列,分为带头结点和不带头结点两种。
②对于入队、出队操作,注意第一个元素入队和最后一个元素出队的操作。
注:下述内容基于带头结点的单链表,队列队尾指针指向队尾元素
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdbool.h>
//队列内元素存储结构
typedef struct QNode{
int data;
struct QNode *next;
} QNode, *QueuePtr;
//队列类型
typedef struct{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
void InitQueue(LinkQueue *Q); //初始化队列Q(带头结点)
bool QueueEmpty(LinkQueue Q); //判断队列是否为空
void EnQueue(LinkQueue *Q, int e); //将元素e放入队尾
int DeQueue(LinkQueue *Q); //移走队头元素,返回该元素的值
int GetFront(LinkQueue Q); //获取队头元素的值,但不从队列中移走该元素
int Length(LinkQueue Q); //计算并返回队列中元素的个数
//初始化队列Q(带头结点)
void InitQueue(LinkQueue *Q){
//初始化时front、rear都指向头结点
Q->front = Q->rear = (QueuePtr)malloc(sizeof(QNode));
if(!Q->front) return;
Q->front->next = NULL;
}
//判断队列是否为空
bool QueueEmpty(LinkQueue Q){
if(Q.front == Q.rear){
return true;
}
else
return false;
}
//将元素e放入队尾
void EnQueue(LinkQueue *Q, int e){
QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
if(!p) return;
p->data = e;
p->next = NULL;
Q->rear->next = p;
Q->rear = p;
}
//移走队头元素,返回该元素的值
int DeQueue(LinkQueue *Q){
//判断队列是否为空
if(Q->rear == Q->front){
printf("队列为空!");
return 0;
}
QueuePtr p = Q->front->next;
int e = p->data; //用变量e存放队头元素
Q->front->next = p->next; //修改头结点的next指针
if(Q->rear == p) //若此次出队列的使队列中的最后一个结点
Q->rear = Q->front; //修改rear指针
free(p); //释放结点空间
return e;
}
//获取队头元素的值,但不从队列中移走该元素
int GetFront(LinkQueue Q){
return Q.front->next->data;
}
//计算并返回队列中元素的个数
int Length(LinkQueue Q){
int n = 0;
QueuePtr p;
p = Q.front;
while(p != Q.rear){
p = p->next;
n++;
}
return n;
}
int main()
{
LinkQueue Q;
InitQueue(&Q);
for(int i = 0; i < 10; i++){
EnQueue(&Q, i);
}
printf("%d ", Length(Q));
printf("%d", GetFront(Q));
return 0;
}
四、双端队列
双端队列:只允许从两端插入、两端删除的线性表。
输入受限的双端队列:只允许一端插入、两端删除的线性表。
输出受限的双端队列:只允许两端插入、一端删除的线性表。
注:对输出序列合法性的判断
在栈中合法的输出序列,在双端队列中必定合法
五、应用
1. 离散时间模拟
2.排队问题
3.作业控制
4.树的层次遍历
5.图的广度优先搜索
6.在操作系统中的应用:多个进程争抢着使用有限的系统资源时,FCFS(First Come First Service,先来先服务)是一种常用策略。