4.队列
·相关概念
-
定义:只能在表尾进行插入操作,在表头进行删除操作的线性表。 (头插尾删)
-
逻辑结构:与线性表相同,仍为一对一关系。
-
存储结构:顺序队和链式队都可,但循环顺序队更常用
-
运算规则:只能在队首和队尾运算,先进先出(FIFO)原则.与线性表不同(随机存取)。
·队列图解
队列示意图
常见算法
1. 初始化,入队,出队图解:
2. 假溢出
概念:尾指针已经指向队列最后,继续入队则会溢出,但由于出队操作导致队列还有有空间,此时称为假溢出(如图)
解决假溢出方法:
-
将队中元素依次向队头方向移动(像现实生活中排队一样)
缺点:浪费时间 -
引入循环队列,队尾指向队头(如图)
base[0]接在base[MAXOSIZE-1]之后,若rear+1==M,表示尾指针已经到了最后面,则令rear=0;
(由于顺序表无法直接令尾部指向头部所以使用一个新的运算,取模运算) |
实现方法:
Q.rear=(Q.rear+1)%MAXQSIZE; // 当尾指针达到最大值时,回到0
Q.front=(Q.front+1)%MAXQSIZE; // 当头指针到达最大值时,回到0
为了避免队空和队满的判断条件一样(如上图),我们采用少用一个元素空间的方式(如下图),类似头结点。
队满为:(rear+1)%MAXSIZE==front
·具体实现
队列的顺序表示:
- 队列的类型定义
#define MAXQSIZE 100 //队列的最大长度
typedef struct {
int *base; //队列的基地址用于动态分配空间
int front; //队列的头指针(下标)
int rear; //队列的尾指针(下标)
}SqQueue;
- 循环顺序队初始化
// 循环顺序队初始化(即构造一个空队列)
Status InitStack(SqQueue &Q){
Q.base=(int*)malloc(MAXQSIZE*sizeof(int)); // 为队列开辟地址(malloc不再赘述)
if(!Q.base)exit(OVERFLOW); // 如果base地址为0则表示没有分配成功
Q.front=Q.rear=0; //头指针等于尾指针都则为空栈
return OK;
}
- 求队列的长度
// 求队列的长度
int QueueLength(SqQueue Q){
return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE; //由于是循环队列,且默认有一个位置为空不存放数据
}
- 循环队列入队
·算法思路:
- 判读是否队满,若满则报错(上溢)
- 元素e赋值给队尾指针所指的数
- 指针++指向下一元素
// 循环队列入队
Status EnQueue(SqQueue &Q,int e){
if((Q.rear-1)%MAXQSIZE==Q.front) // 判断队满
return ERROR;
Q.base[Q.rear]=e; //在队尾插入数
Q.rear=(Q.rear+1)%MAXQSIZE; //队尾指针+1指向下一元素
}
- 循环队列出队
·算法思路:
- 判断是否队空,若空则报错(下溢)
- 获取头指针所指元素,用e返回其指
- 头指针–指向下一元素
// 循环顺序队出队
Status DeQueue(SqQueue &Q,int &e){
if(Q.rear==Q.front) // 判断队空
return ERROR;
e=Q.base[Q.front]; //获取头指针所指元素,用e返回其指
Q.front=(Q.front+1)%MAXQSIZE; //队尾指针+1指向下一元素
return OK;
}
- 取队头元素
// 取队头元素
int GetHead(SqQueue Q){
if(Q.rear==Q.front) // 判断队是否为空
return ERROR;
return Q.base[Q.front];
}
队列的链式表示:
链式实现图解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7cmJTM0-1639559068341)(img_10.png)]
- 链队列的定义
// 链队列结点的定义:
typedef struct QueueNode{
int data;
struct QueueNode *next;
}QueueNode,*QueuePtr;
// 链队列的定义
typedef struct {
QueuePtr front; //头指针
QueuePtr rear; //尾指针
}LinkQueue;
- 队列的初始化
// 链队列的初始化(构建空队列):
Status InitQueue(LinkQueue &Q){
//为头指针为指针开指向的空间并令其相等
Q.front=Q.rear=((QueuePtr)malloc(sizeof(QueueNode)));
Q.front->next=NULL; //让头结点的next为空;
return OK;
}
- 销毁队列
算法思想:从头结点开始依次释放所以结点
// 销毁队列
Status DestroyQueue(LinkQueue &Q){
while(Q.front){
QueuePtr p; // 构造一个指针p用于暂时指向被删结点
p=((QueuePtr)malloc(sizeof(QueueNode)));
p=Q.front->next; //让p暂时指向头指针指向结点的下一个结点
delete Q.front; //删除头指针指向的结点
Q.front=p; //让头指针指向下一个结点;
}
return OK;
}
- 队列的入队
// 队列的入队
Status EnQueue(LinkQueue &Q,int e){
QueuePtr p; // 构造一个指针p用于暂时指向被入队的结点
p=((QueuePtr)malloc(sizeof(QueueNode)));
p->data=e;
p->next=NULL; // 尾插法
Q.rear->next=p; // 让p结点与队尾链接
Q.rear=p; //新结点成为新的队尾
return OK;
}
- 队列的出队
Status DeQueue(LinkQueue &Q,int e){
if(Q.front==Q.rear) //判断队空
return ERROR;
QueuePtr p; // 构造一个指针p用于暂时指向被出队的结点
p=((QueuePtr)malloc(sizeof(QueueNode)));
p=Q.front->next; //指向被出队的结点
e=p->data; //e返回出列结点的值
Q.front->next=p->next; //头结点指向下一个被出队结点
if(Q.rear==p)Q.rear=Q.front; //若尾结点出队,则尾指针指向头结点。
delete p; //释放出队结点的空间
return OK;
}
- 取队头元素
// 取队头元素
int GetHead(LinkQueue Q){
if(Q.rear==Q.front) //判断队是否为空
return ERROR;
return Q.front->next->data;
}