文章目录
- 四 队列
- 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)
{//初始时,队头队尾指针都指向0Q.front = Q.rear = 0;
}//判空
bool QueueEmpty(SqQueue Q)
{if (Q.rear == Q.front)return true;elsereturn 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;elsereturn 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;elsereturn 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都指向NULLQ.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指向NULLQ.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都指向NULLQ.front = NULL;Q.rear = NULL;
}
//判空
bool IsEmpty(LinkQueue& Q)
{if (Q.front == NULL)return true;elsereturn 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指向NULLQ.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.双端队列
-
定义
允许从两端插入删除的线性表。
-
衍生
-
输出受限的双端队列
只允许从一端插入,两端删除。
-
输出受限的双端队列
只允许从两端插入,一端删除。
-