🌹个人主页🌹:喜欢草莓熊的bear
🌹专栏🌹:数据结构
前言
上期博客介绍了” 栈 “这个数据结构,他具有先进后出的特点。本期介绍“ 队列 ”这个数据结构,他具有先进先出的特点。
目录
前言
一、队列
1.1队列的概念及结构
1.2队列的实现
1.2.1队列结构体定义
1.2.2初始化和销毁
1.2.3队尾插入和队头删除
1.2.3.1队尾插入
1.2.3.2队头删除
1.2.4获取队头与队尾数据
1.2.5返回队列里面的元素个数
1.2.6队列判空
1.3测试代码
1.4代码展示
头文件
实现功能文件.c
总结
一、队列
1.1队列的概念及结构
1.2队列的实现
1.2.1队列结构体定义
根据上面提到的我们基于链表(这里的链表代指单链表)来实现队列。所以我就定义以下结构体
typedef int QDataType;
typedef struct QueueNode//我们基于单链表创建的队列节点
{
struct QueueNode* next;//下一节点的地址
QDataType val;
}QNode;
根据内容发现我们要获取队列的队头数据和队尾数据,所以我们要创建两个这样的结构体。由于节点的地址是通过一级指针来表示的,所以要用二级指针来储存,所以我们传参数时要使用二级指针。 记住只要涉及通过形式参数改变实参的都要传地址。
这时我们很牛的杭哥就给了一个思路,那就是再创建一个结构体里面的元素是上一个结构体。这样既满足了队头和队尾指针,还不需要传二级指针。因为有一个计数的功能所以再定义一个计数的变量。
typedef struct Queue //因为我们要传头指针和尾指针,所以我们要传两个指针
//我们又要传二级指针,但是呢很麻烦,所以我们就新创建一个结构体来储存
//上一个结构体的指针。
{
QNode* phead;//队头指针
QNode* ptail;//队尾指针
int size;
}Queue;
1.2.2初始化和销毁
初始化:这一步和上次写的栈的初始化可以说是十分相似,第一步还是判断传来的是否为空队列,把结构体里面的指针置为NULL,再把size赋值位0。就ok了。
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
销毁:因为要申请空间,必定需要free,因为我们是基于链表实现的参考一下单链表的销毁。
所以我们也需要一个一个节点释放,最后把指针指向空指针,把size赋值为0.
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* pur = pq->phead;
while (pur)
{
QNode* cur = pur->next;
free(pur);
pur = cur;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
1.2.3队尾插入和队头删除
1.2.3.1队尾插入
因为我们是基于单链表的实现的,所以只需要创建节点就可以了,通过malloc申请空间。添加一个节点就开辟一块空间。
链表不为空;很简单直接进行尾插操作,不要忘了给size++。
链表为空;那就把头和尾指针同时指向这个新节点。
判别是否为空链表少不了。
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* Newnode = (QNode*)malloc(sizeof(QNode));
if (Newnode == NULL)
{
perror("malloc fail");
return;
}
Newnode->next = NULL;
Newnode->val = x;
if (pq->phead==NULL)//空链表
{
pq->phead = pq->ptail = Newnode;
}
else
{
pq->ptail->next = Newnode;
pq->ptail = Newnode;
}
pq->size++;//用来计数
}
1.2.3.2队头删除
链表中只有一个节点:当phead == ptail 时就只有一个节点了,所以我们把这个节点释放了以后要把phead 和 ptail 都置为空指针,预防出现野指针。
多个节点:我们创建一个节点用来储存头节点的下一个指针,这样释放了头节点也可以找到剩下的节点。对头删除当然要size--。
老生常谈,判断是否为空链表,还要判断是否还可以进行队头删除操作。也就队列里面还有数据吗
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
if (pq->phead == pq->ptail)//只有一个节点
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else//多个节点
{
QNode* tmp = pq->phead->next;
free(pq->phead);
pq->phead = tmp;
}
pq->size--;
}
1.2.4获取队头与队尾数据
很简单返回指针里面储存的数据就可以了,除了判断队列是否为空。还要判断这些头尾指针是否为空的。
QDataType QueueFront(Queue* pq) //返回队头数据
{
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
QDataType QueueBack(Queue* pq) //返回队尾数据
{
assert(pq);
assert(pq->ptail);
return pq->ptail->val;
}
1.2.5返回队列里面的元素个数
这时就可以体现出我们再第二给结构体中创建size的价值了,我们直接返回size。没有创建size我们就需要遍历队列了。
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
1.2.6队列判空
前面创建的szie又帮了大忙,还可以用来判断是否为空对列。
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
1.3测试代码
进行了4个数据的插入,检测了判空、获取队头元素、队头删除。等操作,在这里还是建议大家每完成一个功能就进行测试,不然一起测试出现了很多问题修改起来很麻烦的。作者身有体会,
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"
int main()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
printf("%d ", QueueFront(&q));
QueuePop(&q);
QueuePush(&q, 3);
QueuePush(&q, 4);
while (!QueueEmpty(&q))
{
printf("%d ", QueueFront(&q));
QueuePop(&q);
}
QueueDestory(&q);
return 0;
}
成功实现了队列 “ 先进先出 ”的特点。
1.4代码展示
头文件
Queue.h
#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>
typedef int QDataType;
typedef struct QueueNode//我们基于单链表创建的队列节点
{
struct QueueNode* next;
QDataType val;
}QNode;
typedef struct Queue //因为我们要传头指针和尾指针,所以我们要传两个指针
//我们又要传二级指针,但是呢很麻烦,所以我们就新创建一个结构体来储存
//上一个结构体的指针。
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
//初始化和销毁
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
//队尾插入和队头删除
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
//获取队头、尾数据
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
//返回个数和判空
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);
实现功能文件.c
Queue.c
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* pur = pq->phead;
while (pur)
{
QNode* cur = pur->next;
free(pur);
pur = cur;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* Newnode = (QNode*)malloc(sizeof(QNode));
if (Newnode == NULL)
{
perror("malloc fail");
return;
}
Newnode->next = NULL;
Newnode->val = x;
if (pq->phead==NULL)//空链表
{
pq->phead = pq->ptail = Newnode;
}
else
{
pq->ptail->next = Newnode;
pq->ptail = Newnode;
}
pq->size++;//用来计数
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
if (pq->phead == pq->ptail)//只有一个节点
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else//多个节点
{
QNode* tmp = pq->phead->next;
free(pq->phead);
pq->phead = tmp;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->ptail);
return pq->ptail->val;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
总结
很感谢大家的喜欢,有问题大家在评论区发来我们一起交流。下期预告通过栈和队列互相实现对方的功能。