3.1 队列介绍
1> 队列也是操作受限的线性表:所有操作只能在端点处进行,其删除和插入必须在不同端进行
2> 允许插入操作的一端称为队尾,允许删除操作的一端称为队头
3> 特点:先进先出(FIFO)
4> 分类:
顺序存储的队列称为顺序队列
链式存储的队列,称为链式队列
3.2 顺序队列
1> 使用一片连续存储的空间存储队列,并且给定两个变量,分别记录队头和队尾下标
2> 普通顺序队列使用中,存在“假溢满”现象
假溢满:队列中明明还有存储空间,但是由于队尾已经达到了数组的最大下标,不能在继续入队元素了
3> 为了解决“假溢满”现象,我们引入了循环顺序队列
3.3 循环顺序队列
1> 循环顺序队列:
通过相关操作,当对头或队尾达到数组最大下标时,可以返回到下标为0的位置
2> 结构体类型:
一个数组存储队列,两个变量分别存储队头和队尾的下标
注意:需要人为浪费一个存储空间,用于判满
#define MAX 8 //队列最大长度
typedef int datatype; //数据元素类型
//定义队列类型
typedef struct
{
datatype data[MAX]; //存储队列的容器
int front; //记录队头下标
int tail; //记录队尾下标
}SeqQueue, *SeqQueuePtr;
3> 创建队列
//创建队列
SeqQueuePtr queue_create()
{
//在堆区申请一个队列的大小
SeqQueuePtr Q =(SeqQueuePtr)malloc(sizeof(SeqQueue));
if(NULL==Q)
{
printf("创建失败\n");
return NULL;
}
//初始化
bzero(Q->data, sizeof(Q->data)); //初始化数组
Q->front = Q->tail = 0; //队列为空
printf("队列创建成功\n");
return Q;
}
4> 判空判满
//队列判空
int queue_empty(SeqQueuePtr Q)
{
return Q->front == Q->tail;
}
//队列判满
int queue_full(SeqQueuePtr Q)
{
return (Q->tail+1)%MAX == Q->front;
}
5> 入队:将数据放入队尾所在位置
//入队
void queue_push(SeqQueuePtr Q, datatype e)
{
//判断逻辑
if(NULL==Q || queue_full(Q))
{
printf("入队失败\n");
return ;
}
//入队逻辑
Q->data[Q->tail] = e;
//队列变化:队尾后移
Q->tail = (Q->tail+1)%MAX;
printf("入队成功\n");
}
6> 遍历队
//遍历队列
void queue_show(SeqQueuePtr Q)
{
//判断逻辑
if(NULL==Q || queue_empty(Q))
{
printf("遍历失败\n");
return ;
}
//遍历逻辑
printf("从队头到队尾元素分别是:");
for(int i=Q->front; i!=Q->tail; i=(i+1)%MAX)
{
printf("%d\t", Q->data[i]);
}
printf("\n");
}
7> 出队
//出队
void queue_pop(SeqQueuePtr Q)
{
//判断逻辑
if(NULL==Q || queue_empty(Q))
{
printf("出队失败\n");
return;
}
//出队逻辑
printf("%d出队成功\n", Q->data[Q->front]);
//队头后移
Q->front = (Q->front+1)%MAX;
}
8> 求队列的大小:只用队头和队尾完成
//求队列的大小
int queue_size(SeqQueuePtr Q)
{
//在不使用循环的情况下求大小
//判断逻辑
if(NULL == Q)
{
printf("失败\n");
return -1;
}
return (Q->tail-Q->front+MAX)%MAX; //核心语句
}
9> 销毁队列
//销毁队列
void queue_destroy(SeqQueuePtr Q)
{
if(NULL != Q)
{
free(Q);
Q = NULL;
}
printf("销毁成功\n");
}
3.4 所有代码
1.c
#include"1.h"
int main(int argc, const char *argv[])
{
//调用创建队列函数
SeqQueuePtr Q = queue_create();
if(NULL == Q)
{
return -1;
}
//调用入队函数
queue_push(Q, 520);
queue_push(Q, 1314);
queue_push(Q, 666);
queue_push(Q, 999);
//调用遍历函数
queue_show(Q);
//调用出队函数
queue_pop(Q);
queue_pop(Q);
queue_pop(Q);
queue_pop(Q);
queue_pop(Q);
queue_show(Q);
//销毁队列
queue_destroy(Q);
Q = NULL;
return 0;
}
2.c
#include"1.h"
//创建队列
SeqQueuePtr queue_create()
{
//在堆区申请一个队列的大小
SeqQueuePtr Q =(SeqQueuePtr)malloc(sizeof(SeqQueue));
if(NULL==Q)
{
printf("创建失败\n");
return NULL;
}
//初始化
bzero(Q->data, sizeof(Q->data)); //初始化数组
Q->front = Q->tail = 0; //队列为空
printf("队列创建成功\n");
return Q;
}
//队列判空
int queue_empty(SeqQueuePtr Q)
{
return Q->front == Q->tail;
}
//队列判满
int queue_full(SeqQueuePtr Q)
{
return (Q->tail+1)%MAX == Q->front;
}
//入队
void queue_push(SeqQueuePtr Q, datatype e)
{
//判断逻辑
if(NULL==Q || queue_full(Q))
{
printf("入队失败\n");
return ;
}
//入队逻辑
Q->data[Q->tail] = e;
//队列变化:队尾后移
Q->tail = (Q->tail+1)%MAX;
printf("入队成功\n");
}
//遍历队列
void queue_show(SeqQueuePtr Q)
{
//判断逻辑
if(NULL==Q || queue_empty(Q))
{
printf("遍历失败\n");
return ;
}
//遍历逻辑
printf("从队头到队尾元素分别是:");
for(int i=Q->front; i!=Q->tail; i=(i+1)%MAX)
{
printf("%d\t", Q->data[i]);
}
printf("\n");
}
//出队
void queue_pop(SeqQueuePtr Q)
{
//判断逻辑
if(NULL==Q || queue_empty(Q))
{
printf("出队失败\n");
return;
}
//出队逻辑
printf("%d出队成功\n", Q->data[Q->front]);
//队头后移
Q->front = (Q->front+1)%MAX;
}
//求队列的大小
int queue_size(SeqQueuePtr Q)
{
//在不使用循环的情况下求大小
//判断逻辑
if(NULL == Q)
{
printf("失败\n");
return -1;
}
return (Q->tail-Q->front+MAX)%MAX; //核心语句
}
//销毁队列
void queue_destroy(SeqQueuePtr Q)
{
if(NULL != Q)
{
free(Q);
Q = NULL;
}
printf("销毁成功\n");
}
1.h
#ifndef LOOPSEQQUEUE_H
#define LOOPSEQQUEUE_H
#include<myhead.h>
#define MAX 8 //队列最大长度
typedef int datatype; //数据元素类型
//定义队列类型
typedef struct
{
datatype data[MAX]; //存储队列的容器
int front; //记录队头下标
int tail; //记录队尾下标
}SeqQueue, *SeqQueuePtr;
//创建队列
SeqQueuePtr queue_create();
//队列判空
int queue_empty(SeqQueuePtr Q);
//队列判满
int queue_full(SeqQueuePtr Q);
//入队
void queue_push(SeqQueuePtr Q, datatype e);
//遍历队列
void queue_show(SeqQueuePtr Q);
//出队
void queue_pop(SeqQueuePtr Q);
//求队列的大小
int queue_size(SeqQueuePtr Q);
//销毁队列
void queue_destroy(SeqQueuePtr Q);
#endif
3.5 链式队列
1> 链式存储的队列称为链式队列
2> 实现原理:
单向链表头插尾删实现:链表的头部就是队尾,链表的尾部就是队头
单向链表头删尾插实现:链表的头部就是队头,链表的尾部就是队尾
但是:上述操作中,都要用到链表尾部节点,都需要遍历整个链表完成,效率较低
此时,我们可以引入尾指针的概念,专门指向最后一个节点的指针。
3> 将一个头指针和一个尾指针封装成一个队列
4> 队列类型
//定义数据元素类型
typedef int datatype;
//定义结点类型
typedef struct Node
{
union
{
datatype data; //数据域
int len; //长度
};
struct Node *next; //指针域
}Node, *NodePtr;
//定义队列类型
typedef struct
{
NodePtr head; //头指针
NodePtr tail; //尾指针
}Queue, *QueuePtr;
5> 创建队列
注意:先创建出队列、然后创建链表、将队列的两个指针指向链表
//创建队列
QueuePtr queue_create()
{
//堆区申请一个队列的空间
QueuePtr Q = (QueuePtr)malloc(sizeof(Queue));
if(NULL == Q)
{
printf("创建失败\n");
return NULL;
}
//此时 Q->head 和Q->tail是两个野指针
//创建一个链表
Q->head = (NodePtr)malloc(sizeof(Node));
if(Q->head == NULL)
{
printf("创建失败\n");
free(Q); //释放队列空间
return NULL;
}
//给头结点初始化
Q->head->len = 0;
Q->head->next = NULL;
//将两个指针指向头结点
Q->tail = Q->head;
printf("创建成功\n");
return Q;
}
6> 队列判空
只需要队头和队尾都指向头结点即可
//判空
int queue_empty(QueuePtr Q)
{
return Q->head == Q->tail;
}
7> 入队
//入队
void queue_push(QueuePtr Q, datatype e)
{
//判断逻辑
if(NULL==Q)
{
printf("入队失败\n");
return;
}
//申请结点封装数据
NodePtr p = (NodePtr)malloc(sizeof(Node));
if(NULL==p)
{
printf("入队失败\n");
return;
}
//初始化
p->data = e;
p->next = NULL;
//尾插
Q->tail->next = p; //将结点连接到链表上
//更新尾指针
Q->tail = p;
//表长变化
Q->head->len++;
printf("入队成功\n");
}
8> 遍历队
//遍历队列
void queue_show(QueuePtr Q)
{
//判断逻辑
if(NULL==Q || queue_empty(Q))
{
printf("遍历失败\n");
return ;
}
//定义遍历指针,从第一个结点出发
NodePtr q = Q->head->next;
while(q!=NULL)
{
//只要结点不为空,就输出数据域
printf("%d\t", q->data);
q = q->next; //向后遍历
}
printf("\n");
}
9> 出队
出队时,需要注意,如果所有节点全部出队,则需要将尾指针重新指向头结点
//出队
void queue_pop(QueuePtr Q)
{
//判断逻辑
if(NULL==Q || queue_empty(Q))
{
printf("出队失败\n");
return ;
}
//出队:头删
NodePtr p = Q->head->next; //标记
Q->head->next = p->next; //孤立
printf("%d出队成功\n", p->data);
free(p); //删除
p = NULL;
//判断是否已经全部删除
if(Q->head->next == NULL)
{
//尾指针重新指向头结点
Q->tail = Q->head;
}
//表长变化
Q->head->len--;
}
10> 销毁队伍
//销毁队列
void queue_destroy(QueuePtr Q)
{
//判断逻辑
if(NULL==Q)
{
return;
}
//释放所有的结点
while(!queue_empty(Q))
{
queue_pop(Q); //不断将结点出队
}
//释放头结点
free(Q->head);
Q->head=Q->tail = NULL; //防止野指针
//释放队列空间
free(Q);
Q = NULL;
printf("释放成功\n");
}
11> 求队列长度
//求队列长度
int queue_size(QueuePtr Q)
{
//判断逻辑
if(NULL==Q)
{
return -1;
}
return Q->head->len;
}
3.6 所有代码
1.c
#include"1.h"
//创建队列
QueuePtr queue_create()
{
//堆区申请一个队列的空间
QueuePtr Q = (QueuePtr)malloc(sizeof(Queue));
if(NULL == Q)
{
printf("创建失败\n");
return NULL;
}
//此时 Q->head 和Q->tail是两个野指针
//创建一个链表
Q->head = (NodePtr)malloc(sizeof(Node));
if(Q->head == NULL)
{
printf("创建失败\n");
free(Q); //释放队列空间
return NULL;
}
//给头结点初始化
Q->head->len = 0;
Q->head->next = NULL;
//将两个指针指向头结点
Q->tail = Q->head;
printf("创建成功\n");
return Q;
}
//判空
int queue_empty(QueuePtr Q)
{
return Q->head == Q->tail;
}
//入队
void queue_push(QueuePtr Q, datatype e)
{
//判断逻辑
if(NULL==Q)
{
printf("入队失败\n");
return;
}
//申请结点封装数据
NodePtr p = (NodePtr)malloc(sizeof(Node));
if(NULL==p)
{
printf("入队失败\n");
return;
}
//初始化
p->data = e;
p->next = NULL;
//尾插
Q->tail->next = p; //将结点连接到链表上
//更新尾指针
Q->tail = p;
//表长变化
Q->head->len++;
printf("入队成功\n");
}
//遍历队列
void queue_show(QueuePtr Q)
{
//判断逻辑
if(NULL==Q || queue_empty(Q))
{
printf("遍历失败\n");
return ;
}
//定义遍历指针,从第一个结点出发
NodePtr q = Q->head->next;
while(q!=NULL)
{
//只要结点不为空,就输出数据域
printf("%d\t", q->data);
q = q->next; //向后遍历
}
printf("\n");
}
//出队
void queue_pop(QueuePtr Q)
{
//判断逻辑
if(NULL==Q || queue_empty(Q))
{
printf("出队失败\n");
return ;
}
//出队:头删
NodePtr p = Q->head->next; //标记
Q->head->next = p->next; //孤立
printf("%d出队成功\n", p->data);
free(p); //删除
p = NULL;
//判断是否已经全部删除
if(Q->head->next == NULL)
{
//尾指针重新指向头结点
Q->tail = Q->head;
}
//表长变化
Q->head->len--;
}
//求队列长度
int queue_size(QueuePtr Q)
{
//判断逻辑
if(NULL==Q)
{
return -1;
}
return Q->head->len;
}
//销毁队列
void queue_destroy(QueuePtr Q)
{
//判断逻辑
if(NULL==Q)
{
return;
}
//释放所有的结点
while(!queue_empty(Q))
{
queue_pop(Q); //不断将结点出队
}
//释放头结点
free(Q->head);
Q->head=Q->tail = NULL; //防止野指针
//释放队列空间
free(Q);
Q = NULL;
printf("释放成功\n");
}
2.c
#include"1.h"
int main(int argc, const char *argv[])
{
//调用创建函数
QueuePtr Q = queue_create();
if(NULL==Q)
{
return -1;
}
//调用入队函数
queue_push(Q, 111);
queue_push(Q, 222);
queue_push(Q, 333);
queue_push(Q, 444);
//调用遍历函数
queue_show(Q);
//调用出队函数
queue_pop(Q);
queue_pop(Q);
//释放队列空间
queue_destroy(Q);
Q = NULL;
return 0;
}
1.h
#ifndef LOOPLINKQUEUE_H
#define LOOPLINKQUEUE_H
#include<myhead.h>
//定义数据元素类型
typedef int datatype;
//定义结点类型
typedef struct Node
{
union
{
datatype data; //数据域
int len; //长度
};
struct Node *next; //指针域
}Node, *NodePtr;
//定义队列类型
typedef struct
{
NodePtr head; //头指针
NodePtr tail; //尾指针
}Queue, *QueuePtr;
//创建队列
QueuePtr queue_create();
//判空
int queue_empty(QueuePtr Q);
//入队
void queue_push(QueuePtr Q, datatype e);
//遍历队列
void queue_show(QueuePtr Q);
//出队
void queue_pop(QueuePtr Q);
//求队列长度
int queue_size(QueuePtr Q);
//销毁队列
void queue_destroy(QueuePtr Q);
#endif