队列的定义
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。
进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。
因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。
简而言之:
队列是必须满足先进先出的线性表
准备工作
普通的队列一般用单链表实现
但也可以使用顺序表实现
-本篇文件采用单链表实现
-
创建头文件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; 只能在 队尾 插入数据,所以插入的数据的下一个必定时NULLif (p->head == NULL) 如果队头为空,则队尾也一定为空{p->head = tmp; 让队头指向唯一的一个数据节点p->tail = tmp; 让队尾指向唯一的一个数据节点}else{p->tail->next = tmp; 让队尾指针指向的节点的next指向新节点tmpp->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;//只能在 队尾 插入数据,所以插入的数据的下一个必定时NULLif (p->head == NULL)//如果队头为空,则队尾也一定为空{p->head = tmp;//让队头指向唯一的一个数据节点p->tail = tmp;//让队尾指向唯一的一个数据节点}else{p->tail->next = tmp;//让队尾指针指向的节点的next指向新节点tmpp->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);
}
以上就是全部内容了,如果对你有帮助的话,可以点个赞支持一下!!!