为了避免当只有一个元素时,队头和队尾重合使得处理变得麻烦,所以引入两个指针front
和rear
。
front
即队头指针指向队头元素,rear
即队尾指针指向队尾元素的下一个元素。
这样当front
等于rear
是,不是队列中有一个元素,而是表示空队列。
假设数组长度为5
,空队列即初始状态如图所示,front
和rear
都指向下标为0
的位置。
当队列中有四个元素时,front
不变,rear
指向下标为4
的位置。
若出队两个元素,front
指向下标为2
的位置,rear
不变,再入队一个元素,front
指针不变,但rear
指针移动到数组之外。
假设该队列中数据元素总个数不超过5
个,但是目前如果接着入队的话,会导致数组越界的问题。
但是队列在下标为0
和1
的位置是没有元素的。我们把这个现象叫做"假溢出"。
1. 概念
解决假溢出的办法就是后面满了,再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。
此时如果再入列一个元素,会发现rear
和front
重合了。重合则无法判断其是满还是空。
解决办法:当队列空时,判断条件就是rear == front
,当队列满时,我们修改其判断条件,保留一个元素空闲。也就说,当队列满时,数组中还有一个空闲单元。上面这种情况,我们认为队列已经满了。
为了使得满队和空队能够区分出来,必须牺牲一个数组单元,提前定义满队状态;
假设一个队列有n
个元素,则顺序存储的队列需要建立一个大于n
的数组。
此时一个满队的条件是rear
加一,对循环队列长度取余后,等于front
则说明其是满队。
(rear+1)%N == front
- 逻辑结构:线性结构
- 物理结构:顺序存储结构
2. 接口实现
2.1. 定义操作循环队列的结构体
- 定长数组
- 队头指针
front
- 队尾指针
rear
// 1. 定义操作循环队列的结构体
#define N 10
typedef int DataType;
typedef struct SeqQueue
{
DataType data[N];
int front;
int rear;
} SQ;
2.2. 创建空的循环队列
// 2. 创建一个空的队列
SQ *SQCreate()
{
// 2.1 开辟空间存放操作队列的结构体
SQ *PQ = (SQ *)malloc(sizeof(SQ));
if (NULL == PQ)
{
printf("SQCreate failed, PQ malloc err.\n");
return NULL;
}
// 2.2 初始化结构体成员
PQ->front = PQ->rear = 0;
return PQ;
}
#include "SQ.h"
int main(int argc, char const *argv[])
{
// 2. 创建空的循环队列
SQ *PQ = SQCreate();
return 0;
}
2.3. 入列
- 判满
- 数据入列
- 移动尾指针
// 3. 数据入列
void SQPush(SQ *PQ, DataType data)
{
// 3.1 容错判断——判满
if ((PQ->rear+1)%N == PQ->front)
{
printf("SQPush failed, SQ is full.\n");
return ;
}
// 3.2 数据入列
PQ->data[PQ->rear] = data;
// 3.3 移动尾指针
PQ->rear = (PQ->rear + 1) % N;
}
#include "SQ.h"
int main(int argc, char const *argv[])
{
// 2. 创建空的循环队列
SQ *PQ = SQCreate();
SQPush(PQ, 111);
SQPush(PQ, 222);
SQPush(PQ, 333);
return 0;
}
2.4. 出列
- 容错判断——判空
- 保存出列数据
- 移动队头指针
- 返回出列数据
// 4. 数据出列
DataType SQPop(SQ *PQ)
{
// 4.1 容错判断——判空
if (PQ->front == PQ->rear)
{
printf("SQPop failed, SQ is empty.\n");
return -1;
}
// 4.2 保存出列数据
DataType data = PQ->data[PQ->front];
// 4.3 移动队头指针
PQ->front = (PQ->front+1)%N;
// 4.4 返回出列数据
return data;
}
#include "SQ.h"
int main(int argc, char const *argv[])
{
// 2. 创建空的循环队列
SQ *PQ = SQCreate();
SQPush(PQ, 111);
SQPush(PQ, 222);
SQPush(PQ, 333);
DataType data = SQPop(PQ);
printf("SQPop data is %d\n", data);
// SQPop data is 111
return 0;
}
SQPop data is 111
2.5. 列长
// 5. 列长
int SQLength(SQ *PQ)
{
return (PQ->rear + N - PQ->front) % N;
}
#include "SQ.h"
int main(int argc, char const *argv[])
{
// 2. 创建空的循环队列
SQ *PQ = SQCreate();
SQPush(PQ, 111);
SQPush(PQ, 222);
SQPush(PQ, 333);
DataType data = SQPop(PQ);
printf("SQPop data is %d\n", data);
// SQPop data is 111
int len = SQLength(PQ);
printf("SQLength is %d\n", len);
// SQLength is 2
return 0;
}
SQPop data is 111
SQLength is 2
2.6. 清空
// 6. 清空
void SQClear(SQ *PQ)
{
PQ->front = PQ->rear = 0;
}
#include "SQ.h"
int main(int argc, char const *argv[])
{
// 2. 创建空的循环队列
SQ *PQ = SQCreate();
SQPush(PQ, 111);
SQPush(PQ, 222);
SQPush(PQ, 333);
DataType data = SQPop(PQ);
printf("SQPop data is %d\n", data);
// SQPop data is 111
int len = SQLength(PQ);
printf("SQLength is %d\n", len);
// SQLength is 2
SQClear(PQ);
len = SQLength(PQ);
printf("SQLength is %d\n", len);
// SQLength is 0
free(PQ);
PQ = NULL;
return 0;
}
SQPop data is 111
SQLength is 2
SQLength is 0
2.7. 总结
#ifndef _SQ_H_
#define _SQ_H_
#include <stdio.h>
#include <stdlib.h>
// 1. 定义操作循环队列的结构体
#define N 10
typedef int DataType;
typedef struct SeqQueue
{
DataType data[N];
int front;
int rear;
} SQ;
// 2. 创建一个空的队列
SQ *SQCreate();
// 3. 数据入列
void SQPush(SQ *PQ, DataType data);
// 4. 数据出列
DataType SQPop(SQ *PQ);
// 5. 列长
int SQLength(SQ *PQ);
// 6. 清空
void SQClear(SQ *PQ);
#endif
#include "SQ.h"
// 2. 创建一个空的队列
SQ *SQCreate()
{
// 2.1 开辟空间存放操作队列的结构体
SQ *PQ = (SQ *)malloc(sizeof(SQ));
if (NULL == PQ)
{
printf("SQCreate failed, PQ malloc err.\n");
return NULL;
}
// 2.2 初始化结构体成员
PQ->front = PQ->rear = 0;
return PQ;
}
// 3. 数据入列
void SQPush(SQ *PQ, DataType data)
{
// 3.1 容错判断——判满
if ((PQ->rear+1)%N == PQ->front)
{
printf("SQPush failed, SQ is full.\n");
return ;
}
// 3.2 数据入列
PQ->data[PQ->rear] = data;
// 3.3 移动尾指针
PQ->rear = (PQ->rear + 1) % N;
}
// 4. 数据出列
DataType SQPop(SQ *PQ)
{
// 4.1 容错判断——判空
if (PQ->front == PQ->rear)
{
printf("SQPop failed, SQ is empty.\n");
return -1;
}
// 4.2 保存出列数据
DataType data = PQ->data[PQ->front];
// 4.3 移动队头指针
PQ->front = (PQ->front+1)%N;
// 4.4 返回出列数据
return data;
}
// 5. 列长
int SQLength(SQ *PQ)
{
return (PQ->rear + N - PQ->front) % N;
}
// 6. 清空
void SQClear(SQ *PQ)
{
PQ->front = PQ->rear = 0;
}
#include "SQ.h"
int main(int argc, char const *argv[])
{
// 2. 创建空的循环队列
SQ *PQ = SQCreate();
SQPush(PQ, 111);
SQPush(PQ, 222);
SQPush(PQ, 333);
DataType data = SQPop(PQ);
printf("SQPop data is %d\n", data);
// SQPop data is 111
int len = SQLength(PQ);
printf("SQLength is %d\n", len);
// SQLength is 2
SQClear(PQ);
len = SQLength(PQ);
printf("SQLength is %d\n", len);
// SQLength is 0
free(PQ);
PQ = NULL;
return 0;
}
标签:PQ,队列,SQ,循环,front,data,rear
From: https://blog.51cto.com/u_16225617/7079259