文章目录
- 四 队列
- 1.基本概念
- 2.队列的顺序存储
- 3.队列的链式实现
- 3.1 定义
- 3.2 带头结点
- 3.2.1 初始化
- 3.2.2 判空
- 3.2.3 入队
- 3.2.4 出队
- 3.2.5 完整代码
- 3.3 不带头结点
- 3.3.1 初始化
- 3.3.2 入队
- 3.3.3 出队
- 3.3.4 完整代码
- 4.双端队列
四 队列
1.基本概念
-
定义
只允许在一端进行插入,在另一端删除的线性表。
-
术语
- 队头:允许删除的一端。
- 队尾:允许插入的一端。
-
特点:先进先出
2.队列的顺序存储
-
定义
#define MaxSize 10 #define ElemType int //定义 typedef struct { ElemType data[MaxSize];//用静态数组存放队列元素 int front, rear;//队头指针和队尾指针 }SqQueue;
-
循环队列
搞成循环队列可以节约存储空间。
避免出现队尾指针
Q.rear
已经到MaxSize
时,队头指针Q.front
前有剩余空间的情况。
-
在循环队列下,队列空的条件是
Q.rear==Q.front
-
在循环队列下,队列已满的条件是
(Q.rear+1)%MaxSize==Q.front
即尾指针加一等于头指针时,队列已满。
注:此时会浪费一个存储空间(队尾指针没有存数据)。
- 如果不浪费,即尾指针rear也存元素,则队满和队空判断条件相同,我们无法用逻辑表达区分两种情况。
-
如果题目要求不能浪费一点点存储空间,则将队列定义为
typedef struct { ElemType data[MaxSize];//用静态数组存放队列元素 int front, rear;//队头指针和队尾指针 int size;//队列大小 }SqQueue;
- 此时队满条件:
size==MaxSize
- 队空条件:
size==0
- 此时队满条件:
-
完整代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MaxSize 10
#define ElemType int
//定义
typedef struct {
ElemType data[MaxSize];//用静态数组存放队列元素
int front, rear;//队头指针和队尾指针
}SqQueue;
//初始化队列
void InitQueue(SqQueue& Q)
{
//初始时,队头队尾指针都指向0
Q.front = Q.rear = 0;
}
//判空
bool QueueEmpty(SqQueue Q)
{
if (Q.rear == Q.front)
return true;
else
return false;
}
//创建
bool CreatQueue(SqQueue& Q)
{
int n = 5;
srand(time(0));
while (n--)
{
//队满则报错
//队满条件:队尾指针的下一个位置是队头
if ((Q.rear + 1) % MaxSize == Q.front)
return false;
Q.data[Q.rear] = rand() % 100 + 1;//新元素插入队尾
//将存储空间在逻辑上变成环状(循环队列)
Q.rear = (Q.rear + 1) % MaxSize;//队尾指针加1取模
}
return true;
}
//入队
bool EnQueue(SqQueue& Q, ElemType x)
{
//队满则报错
//队满条件:队尾指针的下一个位置是队头
if ((Q.rear + 1) % MaxSize == Q.front)
return false;
Q.data[Q.rear] = x;//新元素插入队尾
//将存储空间在逻辑上变成环状(循环队列)
Q.rear = (Q.rear + 1) % MaxSize;//队尾指针加1取模
return true;
}
//出队
bool DeQueue(SqQueue& Q, ElemType& x)
{
//判空
if (Q.rear == Q.front)return false;
x = Q.data[Q.front];//x存出队的元素
Q.front = (Q.front + 1) % MaxSize;//头指针后移
return true;
}
//获取队头元素,用x返回
bool GetHead(SqQueue Q, ElemType& x)
{
if (Q.rear == Q.front)return false;
x = Q.data[Q.front];
return true;
}
void QPrint(SqQueue Q)
{
for (int i = Q.front; i < Q.rear-1; i=(i+1) % MaxSize)
{
printf("%d,", Q.data[i]);
}
printf("%d\n", Q.data[Q.rear-1]);
}
int main()
{
SqQueue Q;
InitQueue(Q);
printf("创建:\n");
CreatQueue(Q);
QPrint(Q);
printf("\n");
printf("请输入入队元素:\n");
int x;
scanf_s("%d", &x);
EnQueue(Q, x);
QPrint(Q);
printf("\n");
int y;
DeQueue(Q, y);
printf("出队元素:%d\n", y);
QPrint(Q);
printf("\n");
int z;
GetHead(Q, z);
printf("队头元素:%d\n", z);
}
3.队列的链式实现
3.1 定义
分两个结构体
1.节点结构体:存节点数据和下一个节点地址。
2.队列结构体:存队列的头尾指针。
//定义
//链式队列节点
typedef struct LinkNode {
ElemType data;
struct LinkNode* next;
}LinkNode;
//链式队列
typedef struct {
LinkNode* front;//指向队列队头的指针
LinkNode* rear;//指向队列队尾的指针
}LinkQueue;
- 可分为带头结点和不带头结点,两者功能差不多,具体看题目要求选哪个。
- 因为是链表,所以不存在队满的时候,也不需要用循环队列。
3.2 带头结点
3.2.1 初始化
1.创建一个头节点;
2.将头尾指针front和rear都指向头节点;
3.头节点的next赋空值NULL。
//初始化队列
void InitQueue(LinkQueue& Q)
{
//初始时 front、rear都指向头结点
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
Q.front->next = NULL;
}
3.2.2 判空
当头尾指针都指向同一节点时,链表为空。
//判空
bool IsEmpty(LinkQueue& Q)
{
if (Q.front == Q.rear)
return true;
else
return false;
}
3.2.3 入队
-
步骤
1.创建新节点,存数据+初始化指针域。
2.新节点插入到原rear之后。
3.更新rear,即使尾结点指向新插入节点。
void EnQueue(LinkQueue& Q, ElemType x)
{
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
Q.rear->next = s;//新节点插入到rear之后
Q.rear = s;//修改表尾指针
}
3.2.4 出队
根据队列中是否只有一个结点分两种情况。
-
有多个结点时
1.头结点和出队结点的下一个结点相连。
2.释放出队结点。
-
只有一个结点时
1.尾结点指向头结点。
2.释放出队结点。
- 此时相当于空队列。
bool DeQueue(LinkQueue& Q, ElemType& x)
{
//空队
if (Q.front == Q.rear)return false;
LinkNode* p = Q.front->next;
x = p->data;//用变量x返回队头元素
Q.front->next = p->next;//修改头指针的next指针,即连上出队节点的下一个节点
//如果出队节点是尾节点,即队列中只有一个节点
if (Q.rear == p)
Q.rear = Q.front;//变成空队列
free(p);//释放出队节点
return true;
}
3.2.5 完整代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ElemType int
//定义
//链式队列节点
typedef struct LinkNode {
ElemType data;
struct LinkNode* next;
}LinkNode;
//链式队列
typedef struct {
LinkNode* front;//指向队列队头的指针
LinkNode* rear;//指向队列队尾的指针
}LinkQueue;
//初始化队列
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)
return true;
else
return false;
}
//创建
bool CreatQueue(LinkQueue& Q)
{
int n = 5;
srand(time(0));
while (n--)
{
LinkNode* t = (LinkNode*)malloc(sizeof(LinkNode));
t->data = rand() % 100 + 1;
t->next = NULL;
Q.rear->next = t;
Q.rear = t;
}
return true;
}
//新元素入队
void EnQueue(LinkQueue& Q, ElemType x)
{
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
Q.rear->next = s;//新节点插入到rear之后
Q.rear = s;//修改表尾指针
}
//出队
//用x返回元素
bool DeQueue(LinkQueue& Q, ElemType& x)
{
//空队
if (Q.front == Q.rear)return false;
LinkNode* p = Q.front->next;
x = p->data;//用变量x返回队头元素
Q.front->next = p->next;//修改头指针的next指针,即连上出队节点的下一个节点
//如果出队节点是尾节点,即队列中只有一个节点
if (Q.rear == p)
Q.rear = Q.front;//变成空队列
free(p);//释放出队节点
return true;
}
void QPrint(LinkQueue&Q)
{
LinkNode* p = Q.front->next;
while (p != Q.rear)
{
printf("%d ", p->data);
p = p->next;
}
printf("%d\n\n", Q.rear->data);
}
int main()
{
LinkQueue Q;
InitQueue(Q);
CreatQueue(Q);
QPrint(Q);
printf("请输入要入队的元素:\n");
int x;
scanf_s("%d", &x);
EnQueue(Q, x);
QPrint(Q);
int y;
DeQueue(Q, y);
printf("出队元素为:%d\n", y);
QPrint(Q);
}
3.3 不带头结点
3.3.1 初始化
//初始化队列
void InitQueue(LinkQueue& Q)
{
//初始时 front、rear都指向NULL
Q.front = NULL;
Q.rear = NULL;
}
3.3.2 入队
根据入队元素是否是队列中第一个结点分两种情况。
-
如果入队结点是第一个结点
则头尾指针都指向入队结点。
-
如果不是
则新结点插入到队尾结点之后,并更新队尾结点
void EnQueue(LinkQueue& Q, ElemType x)
{
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
//在空队列中插入第一个元素
if (Q.front == NULL)
{
Q.front = s;//队头队尾指针都指向第一个元素
Q.rear = s;
}
else
{
Q.rear->next = s;//新节点插入到队尾节点之后
Q.rear = s;//更新队尾节点
}
}
3.3.3 出队
- 如果是最后一个结点出队,则需要恢复成空队状态。
bool DeQueue(LinkQueue& Q, ElemType& x)
{
//空队
if (Q.front == NULL)
return false;
LinkNode* p = Q.front;//p指向此次出队的节点
x = p->data;//用变量x返回队头元素
Q.front = p->next;//头指针指向出队节点的下一个节点
//如果是最后一个节点出队
//恢复成空队状态
if (Q.rear == p)
{
Q.front = NULL;//front指向NULL
Q.rear = NULL;//rear指向NULL
}
free(p);//释放节点空间
return true;
}
3.3.4 完整代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ElemType int
//定义
//链式队列节点
typedef struct LinkNode {
ElemType data;
struct LinkNode* next;
}LinkNode;
//链式队列
typedef struct {
LinkNode* front;//指向队列队头的指针
LinkNode* rear;//指向队列队尾的指针
}LinkQueue;
//初始化队列
void InitQueue(LinkQueue& Q)
{
//初始时 front、rear都指向NULL
Q.front = NULL;
Q.rear = NULL;
}
//判空
bool IsEmpty(LinkQueue& Q)
{
if (Q.front == NULL)
return true;
else
return false;
}
//创建
bool CreatQueue(LinkQueue& Q)
{
int n = 5;
srand(time(0));
while (n--)
{
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = rand() % 100 + 1;
s->next = NULL;
//在空队列中插入第一个元素
if (Q.front == NULL)
{
Q.front = s;//队头队尾指针都指向第一个元素
Q.rear = s;
}
else
{
Q.rear->next = s;//新节点插入到队尾节点之后
Q.rear = s;//更新队尾节点
}
}
return true;
}
//入队
void EnQueue(LinkQueue& Q, ElemType x)
{
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
//在空队列中插入第一个元素
if (Q.front == NULL)
{
Q.front = s;//队头队尾指针都指向第一个元素
Q.rear = s;
}
else
{
Q.rear->next = s;//新节点插入到队尾节点之后
Q.rear = s;//更新队尾节点
}
}
//出队
bool DeQueue(LinkQueue& Q, ElemType& x)
{
//空队
if (Q.front == NULL)
return false;
LinkNode* p = Q.front;//p指向此次出队的节点
x = p->data;//用变量x返回队头元素
Q.front = p->next;//头指针指向出队节点的下一个节点
//如果是最后一个节点出队
//恢复成空队状态
if (Q.rear == p)
{
Q.front = NULL;//front指向NULL
Q.rear = NULL;//rear指向NULL
}
free(p);//释放节点空间
return true;
}
void QPrint(LinkQueue& Q)
{
LinkNode* p = Q.front;
while (p != Q.rear)
{
printf("%d ", p->data);
p = p->next;
}
printf("%d\n\n", Q.rear->data);
}
int main()
{
printf("不带头节点:\n");
LinkQueue Q;
InitQueue(Q);
CreatQueue(Q);
QPrint(Q);
printf("请输入要入队的元素:\n");
int x;
scanf_s("%d", &x);
EnQueue(Q, x);
QPrint(Q);
int y;
DeQueue(Q, y);
printf("出队元素为:%d\n", y);
QPrint(Q);
}
4.双端队列
-
定义
允许从两端插入删除的线性表。
-
衍生
-
输出受限的双端队列
只允许从一端插入,两端删除。
-
输出受限的双端队列
只允许从两端插入,一端删除。
-