详解队列的接口函数

文章目录

  • 队列的定义
  • 准备工作
    • 创建头文件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);
}

以上就是全部内容了,如果对你有帮助的话,可以点个赞支持一下!!!

猜你喜欢

转载自blog.csdn.net/2301_80058734/article/details/136415884