文章目录
- 队列的定义
- 准备工作
-
- 创建头文件Stack.h
- 创建源文件Stack.c
- 头文件的包含
- 定义保存队列数据的结构体
- 定义维护队列的队头和队尾指针的结构体
- 初始化函数
- 从队尾插入数据
-
- 图解
- 删除队头数据
-
- 图解
- 取队头数据(只取不删)
- 取队尾数据(只取不删)
- 统计队列的数据个数
- 判断队列是否为空
- 销毁队列
- 全部代码
-
- Queue.h
- Queue.c
队列的定义
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。
进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。
因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。
简而言之:
队列是必须满足先进先出的线性表
准备工作
普通的队列一般用单链表实现
因为可以用单链表的第一个节点做队头,头删是时间复杂度为O(1),而且队列只有尾插(队尾插入),不用尾删
所以只要创建一个尾指针,就可以直接以时间复杂度为O(1)完成插入工作
但也可以使用顺序表实现,但不好
因为数组删除数据需要挪动数据,删除时间复杂度比起链表多很多
-本篇文件采用单链表实现
-
创建头文件Stack.h
将头文件和函数定义等放在Stack.h中,方便管理
创建源文件Stack.c
将接口函数的实现放在里面,方便管理
头文件的包含
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
- stdio.h:用于标准输入输出等
- assret.h:用于使用
assret
函数,实现报错 - stdbool.h:用于使用布尔(bool)类型
- stdlib.h:用于使用动态内存管理函数
定义保存队列数据的结构体
为什么要将队列里的数据的数据类型重命名?
这是为了以后如果改变了SL结构体中数据存储的类型时,
不用到处改函数参数等地方的数据类型,只要改typedef后的int 为对应要改成的数据类型就可以。
至于给结构体重命名则仅是为了方便使用
定义维护队列的队头和队尾指针的结构体
定义该结构体后,使用该结构体再定义一个结构变量,将其初始化后便可用其操作队列
初始化函数
从队尾插入数据
void QueuePush(Queue* p, QuDataType x)
{
assert(p); 如果传入的p是NULL,则属于操作错误,assert就会进行报错
QN* tmp = (QN*)malloc(sizeof(QN));
if (tmp == NULL) 如果tmp为NULL则动态内存申请失败,结束程序
{
printf("malloc失败!");
exit(-1); 结束程序
}
tmp->data = x;
tmp->next = NULL; 只能在 队尾 插入数据,所以插入的数据的下一个必定时NULL
if (p->head == NULL) 如果队头为空,则队尾也一定为空
{
p->head = tmp; 让队头指向唯一的一个数据节点
p->tail = tmp; 让队尾指向唯一的一个数据节点
}
else
{
p->tail->next = tmp; 让队尾指针指向的节点的next指向新节点tmp
p->tail = tmp; 因为新节点tmp成为了最后一个节点,
所以将队尾指针指向更新成tmp
}
}
图解
删除队头数据
图解
取队头数据(只取不删)
取队尾数据(只取不删)
统计队列的数据个数
判断队列是否为空
销毁队列
全部代码
Queue.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QuDataType;
typedef struct QueueNode
{
QuDataType data;
struct QueueNode* next;
}QN;
typedef struct Queue
{
QN* head;
QN* tail;
}Queue;
//初始化
void QueueInit(Queue* p);
//队尾入
void QueuePush(Queue* p, QuDataType x);
//队头出(删一个队头)
void QueuePop(Queue* p);
//取队头数据(只取不删)
QuDataType QueueFront(Queue* p);
//取队尾数据(只取不删)
QuDataType QueueBack(Queue* p);
//队列的数据个数
int QueueSize(Queue* p);
//判断队列是否为空
bool QueueEmpty(Queue* p);
//销毁队列
void QueueDestory(Queue* p);
Queue.c
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"
void QueueInit(Queue* p)
{
assert(p);//如果传入的p是NULL,则属于操作错误,assert就会进行报错
p->head= NULL;//队列为空时队头指针为空
p->tail= NULL;//队列为空时队尾也指针为空
}
//队尾入
void QueuePush(Queue* p, QuDataType x)
{
assert(p);//如果传入的p是NULL,则属于操作错误,assert就会进行报错
QN* tmp = (QN*)malloc(sizeof(QN));
if (tmp == NULL)//如果tmp为NULL则动态内存申请失败,结束程序
{
printf("malloc失败!");
exit(-1);//结束程序
}
tmp->data = x;
tmp->next = NULL;//只能在 队尾 插入数据,所以插入的数据的下一个必定时NULL
if (p->head == NULL)//如果队头为空,则队尾也一定为空
{
p->head = tmp;//让队头指向唯一的一个数据节点
p->tail = tmp;//让队尾指向唯一的一个数据节点
}
else
{
p->tail->next = tmp;//让队尾指针指向的节点的next指向新节点tmp
p->tail = tmp;//因为新节点tmp成为了最后一个节点,
//所以将队尾指针指向更新成tmp
}
}
//队头出(删一个队头)
void QueuePop(Queue* p)
{
assert(p);//如果传入的p是NULL,则属于操作错误,assert就会进行报错
assert(p->head);//队头为空 即队列为空,队列为空时不能再删除
//队列只有一个数据
if (p->head == p->tail)//队头指针和队尾指针指向同一节点时
{
//队列只有一个节点
p->tail = NULL;//删除最后一个节点后,队尾指针应该置空
}
//一般情况和特殊情况(队列只有一个数据)都可处理
QN* tmp = p->head->next;//让tmp指向队的下一个节点
free(p->head);//释放队头
p->head = tmp;//让原队头的下一个节点成为新的队头
}
//取队头数据(只取不删)
QuDataType QueueFront(Queue* p)
{
assert(p);//如果传入的p是NULL,则属于操作错误,assert就会进行报错
assert(p->head);//队头为空 即队列为空,队列为空时取不出数据
return p->head->data;//将 队头 指针指向节点的 数据返回出去
}
//取队尾数据(只取不删)
QuDataType QueueBack(Queue* p)
{
assert(p);//如果传入的p是NULL,则属于操作错误,assert就会进行报错
assert(p->tail);//队头为空 即队列为空,队列为空时取不出数据
return p->tail->data;//将 队尾 指针指向节点的 数据返回出去
}
//统计队列的数据个数
int QueueSize(Queue* p)
{
assert(p);//如果传入的p是NULL,则属于操作错误,assert就会进行报错
QN* cur = p->head;//定义cur接收队头指针,用来遍历链表
int count=0;//定义count用来统计数据个数
while (cur)//cur不为空,就继续循环
{
count++;
cur = cur->next;//让cur指向下一个节点
}
return count;
}
//判断队列是否为空
bool QueueEmpty(Queue* p)
{
assert(p);//如果传入的p是NULL,则属于操作错误,assert就会进行报错
return p->head == NULL;//如果队头指针为空,队列就为空
//队头为空时就return ture
//队头不为空就 return false
}
//销毁队列
void QueueDestory(Queue* p)
{
assert(p);//如果传入的p是NULL,则属于操作错误,assert就会进行报错
if (p->head == NULL)//如果队头为空,队列就已经为空了,不用再销毁
return;
QN* cur = p->head;//定义cur接收队头指针,用来遍历链表
QN* next = p->head->next;//定义next接收cur的下一个节点,用来存储下一个节点的地址
p->head = NULL;//销毁队列后 队头指针要置空
p->tail = NULL;//销毁队列后 队尾指针要置空
while (next)//next不为空就继续循环
{
free(cur);//释放cur指向的节点
cur = next;//让cur接收next中存储的下一个节点的地址
next = next->next;//让next指向next的节点的下一个节点
}
free(cur);
}
以上就是全部内容了,如果对你有帮助的话,可以点个赞支持一下!!!