数据结构之队列
作者:互联网
@
目录前言
陆陆续续的,我们已经学完了顺序表,单链表,双链表以及栈.今天,博主更新的内容就是数据结构中的队列.
1. 何为队列
队列:
只
允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
的特性
只
允许插入数据操作的一端叫做队尾
只
允许删除数据操作的一端叫做队头
而删除操作叫做
出队
,插入操作叫做入队
上面的所有特性,博主用下面一张图给大家演示:
2. 怎样实现队列
既然想要实现队列,我们就需要根据
需求
抓取供应
.那么我们队列的主要需求是什么呢? 没错,答案就是
入队
和出队
入队
,毫无疑问是尾插操作,用顺序表实现非常方便.
出队
,毫无疑问是头删操作,用链表实现非常方便.也就是说只考虑目前操作的好坏,链表和顺序组持平,那我们再考虑更优可能性吧,嗯~~~.链表比顺序表跟节约空间
所以我们选择用链表实现队列
但是用链表实现的话,尾插操作就比较麻烦了(需要遍历到尾部),怎么解决这个麻烦呢,这里采用再开辟一个结构体,用来包含链表结点,新的结构体中只有头尾指针,代码实现请看下一标题
3. 项目搭建
博主这里用的VS2019,就用它进行演示:
先建立
Queue.h
,Queue.c
和test.c
三个文件
Queue.h
的作用是写文件引用,函数声明
Queue.c
的作用是写文件函数的定义
test.c
的作用是测试所写操作是否正确
4. 定义队列
在2标题内容下,已经说明了实现队列最好选择链表链式结构,并单独开一个结构体进行包含,所以下面就是开始这样的实现
typedef int QDataType;
typedef struct QueueNode //定义队列结点
{
QDataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue //定义队列
{
QueueNode* head; //队头
QueueNode* tail; //队尾
}Queue;
5. 队列的所有操作
对于队列来说,我们用到的操作最多的就是下面几种:
入队(尾插), 出队(头删),取队头元素,取队尾元素,判空,获取队列元素数量,初始化和销毁空间
所以,博主先在Queue.h
文件中声明所有方法,后续再详细实现
void QueueInit(Queue* pq);
bool QueueEmpty(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
int QueueSize(Queue* pq);
void QueueDestory(Queue* pq);
5.1 队列之初始化
我们创建一个队列后,队列的头尾指针还是野指针,所以我们将其初始化为NULL.
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
}
测试是否成功:
成功!!
5.2 队列之判空
判空,即判断队列是否为空,怎么判断呢? 只要头指针没有指向任何空间就说明队列为空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
5.3 队列之入队
入队,即尾插,在前面的链表和顺序章节中,我们对这个操作还是很熟悉的:
第一步,开辟一个新空间存储数据
第二步,
tail->next
指向新结点. (只是这里需要注意当链表为空时候)
void QueuePush(Queue* pq, QDataType elem)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if(newnode == NULL)
{
perror("空间申请失败原因:");
exit(-1);
}
newnode->data = elem;
newnode->next = NULL;
//注意这里队列是否为空
pq->head == NULL ? (pq->tail = newnode ):(pq->tail->next = newnode,pq->tail = pq->tail->next);
}
测试:
成功!!!
5.4 队列之出队
很明显,出队即头删,对于头删操作我们也是比较熟悉的:
第一步,先保存下一个结点
第二步,释放头结点
第三步,指向所保存的下一个结点
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq)); //如果队列为空,不能删除
QueueNode* next = pq->head->next;//保存下一个结点
free(pq->head);//释放
pq->head = next;//指向下一个结点
}
测试是否成功:
成功.....????了吗,大家仔细看看上面的代码,想想哪里会出Bug
.
没错,当这样出队到最后只剩下一个结点时候,tail
将会是一个野指针,如下图:
就会发现,当剩下最后一个时候,tail还是指向了原来位置,形成一个野指针
修改代码:
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq)); //如果队列为空,不能删除
QueueNode* next = NULL;
pq->head->next == NULL?
(free(pq->head),pq->head = pq->tail = NULL):
(next = pq->head->next, free(pq->head),pq->head = next);//条件表达式
}
现在测试,才是真的成功
5.5 队列之获取队首
这个比较简单,直接返回即可
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq)); //不能为空
return pa->head->data;
}
5.6 队列之获取队尾
同理,直接返回
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq)); //不能为空
return pa->tail->data;
}
测试是否成功:
成功!!
5.7 队列之返回队列元素数量
这个直接开一个变量
num
,然后遍历队列,进行计数
int QueueSize(Queue* pq)
{
assert(pq);
int num = 0;
QueueNode* cur = pq->head;
while(cur)
{
num++;
cur = cur->next;
}
return num;
}
5.8 队列之销毁空间
挨个销毁每个空间
void QueueDestory(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
测试是否成功:
成功!!!
总代码
Queue.h
头文件
#pragma once
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int QDataType;
typedef struct QueueNode //定义队列结点
{
QDataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue //定义队列
{
QueueNode* head; //队头
QueueNode* tail; //队尾
}Queue;
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
void QueuePush(Queue* pq, QDataType elem);
void QueuePop(Queue* pq);
int QueueSize(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
Queue.c
源文件
#include "Queue.h"
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType elem)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
perror("空间申请失败原因:");
exit(-1);
}
newnode->data = elem;
newnode->next = NULL;
//注意这里队列是否为空
pq->head == NULL ?
(pq->tail = pq->head = newnode) : (pq->tail->next = newnode, pq->tail = pq->tail->next);
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq)); //如果队列为空,不能删除
QueueNode* next = NULL;
pq->head->next == NULL ?
(free(pq->head), pq->head = pq->tail = NULL) :
(next = pq->head->next, free(pq->head), pq->head = next);//条件表达式
}
int QueueSize(Queue* pq)
{
assert(pq);
int num = 0;
QueueNode* cur = pq->head;
while (cur)
{
num++;
cur = cur->next;
}
return num;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
标签:head,pq,队列,next,Queue,数据结构,QueueNode 来源: https://www.cnblogs.com/lytdw123/p/15170426.html