数据结构之队列(代码片段)

捕获一只小肚皮 捕获一只小肚皮     2022-12-19     600

关键词:

前言

陆陆续续的,我们已经学完了顺序表,单链表,双链表以及栈.今天,博主更新的内容就是数据结构中的队列.


1. 何为队列


队列:允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特性

允许插入数据操作的一端叫做队尾

允许删除数据操作的一端叫做队头

而删除操作叫做出队,插入操作叫做入队

上面的所有特性,博主用下面一张图给大家演示:

2. 怎样实现队列


既然想要实现队列,我们就需要根据需求抓取供应.

那么我们队列的主要需求是什么呢? 没错,答案就是 入队出队

入队,毫无疑问是尾插操作,用顺序表实现非常方便.

出队,毫无疑问是头删操作,用链表实现非常方便.

也就是说只考虑目前操作的好坏,链表和顺序组持平,那我们再考虑更优可能性吧,嗯~~~.链表比顺序表跟节约空间

所以我们选择用链表实现队列

但是用链表实现的话,尾插操作就比较麻烦了(需要遍历到尾部),怎么解决这个麻烦呢,这里采用再开辟一个结构体,用来包含链表结点,新的结构体中只有头尾指针,代码实现请看下一标题


3. 项目搭建


博主这里用的VS2019,就用它进行演示:

先建立Queue.h,Queue.ctest.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;

数据结构之栈队列循环队列(代码片段)

二、数据结构之栈、队列、循环队列顺序栈Stack.h结构类型,函数声明:#ifndef_STACK_H_#define_STACK_H_typedefintSElementType;///顺序栈#defineSTACK_INIT_SIZE20#defineSTACK_INCREMENT10typedefstructSElementType*base;SElementType*top;intsta 查看详情

数据结构之队列(代码片段)

队列就是先进先出(fifo),就像排队。栈只支持两个基本操作:入栈push()和出栈pop()队列跟栈非常相似,支持的操作也很有限,最基本的操作也是两个:入队enqueue(),放队列尾部;出队dequeue(),从队列头部取一个元素。所以队列... 查看详情

数据结构之队列(代码片段)

文章目录队列的概念及结构队列的实现思路项目文件的创建队列功能的实现队列的定义队列的初始化队列的判空队列的入队队列的出队获取队首元素获取队尾元素获取队列元素个数队列的销毁总结队列的概念及结构队列:只允许... 查看详情

数据结构之队列(代码片段)

3数据结构之队列3.1什么是队列Queue也是一种线性结构,相比数组,队列的操作是数组的子集。只能从队尾添加元素,从队首取出元素。和生活中的排队是一样的,先到先得。  LinkedList类实现了Queue接口,因此我们可以把Lin... 查看详情

数据结构(10)---队列之环形队列(代码片段)

...实现第二种实现什么是环形队列环形队列也是队列的一种数据结构,也是在队头出队,队尾入队;只是环形队列的大小是确定的,不能进行一个长度的增加,当你把一个环形队列创建好之后,它能存放的元素个数是确定的;一般我们实现... 查看详情

(c语言)手撕数据结构之——队列(代码片段)

目录队列的概念及结构队列的实现方式数组队列链式队列链式队列的实现初始化队列入队列出队列获取队列头部元素获取队列尾部元素获取队列中有效元素个数检测队列是否为空销毁队列实现队列的全部代码测试用例队列的概念... 查看详情

数据结构之队列(代码片段)

队列  1.定义:队列是一种先进先出(FIFO)的线性表。它只允许在表的一端进行插入,而在另一端删除元素。在队列中,允许插入的一端叫做队尾,允许删除的一端则称为队头。  假设对列为q=(a1,a2,a3,...,an),那... 查看详情

数据结构之队列(代码片段)

前言: 前面,我们学习了栈,单链表,双向链表,顺序表的内容。大家可以看看我上面写过的文章!栈双链表单链表动态顺序表静态顺序表目录一.什么是队列二.项目创建三.队列代码实现1.队列结构体的... 查看详情

day5:数据结构之队列(代码片段)

队列前言1、队列的概念2、队列的实现方式3、队列的基本操作头文件1.初始化队列2.队尾入队列3.队头出队列4.获取队列头部元素5.获取队列队尾元素6.获取队列有效个数7.判断队列为空8.销毁队列4、队列面试题(结合栈)5... 查看详情

systemvipc之消息队列(代码片段)

...一条消息,也可以请求接收下一条特定类型的消息。相关数据结构与其他两个SystemVIPC通信机制一样,消息队列也有一个与之对应的结构,该结构的定义如下:structmsqid_d 查看详情

数据结构之栈与队列(代码片段)

...列  栈与队列是程序设计中广泛使用的两种重要的线性数据结构。  栈是LIFO(LastInFirstOut),先存进去的数据只能最后被取出来,进出顺序逆序,即先进后出,后进先出。      队列是FIFO(FirstInFirstOut... 查看详情

数据结构之队列(代码片段)

  数据结构中队列是一种线性的存储结构,该结构的特性是先进先出(将首先处理添加到队列的第一个元素),操作步骤如下图所示:  该结构的具体实现是:初始化队列,提供一个list集合data,并为该集合设置一个p_start属... 查看详情

数据结构之链式队列的代码实现及有趣应用(代码片段)

目录背景基本概念结点代码实现链式队列代码实现链式队列的应用代码分析背景队列[Queue]:是一种限定仅在表头进行删除操作,仅在表尾进行插入操作的线性表;即先进先出(FIFO-firstinfirstout):最先插入的元素最先出来。本文... 查看详情

第五节go数据结构之队列(代码片段)

一、什么是队列数据结构里的队列就是模仿现实中的排队。如上图中狗狗排队上厕所,新来的狗狗排到队伍最后,最前面的狗狗撒完尿走开,后面的跟上。可以看出队列有两个特点:(1)新来的都排在队尾;(2)最前面的办理... 查看详情

数据结构之守规矩的先进先出的队列(代码片段)

一、队列的概念  队列是一种特殊的线性表,严格按照“先进先出”的原则。二、队列基础  队列和栈一样只允许在断点处插入和删除元素,其基本操作包括以下:  (1)InitQueue(&Q);  //初始化一个空队列  (... 查看详情

数据结构之队列(代码片段)

队列队列是一个有序列表,可以用数组或者链表来实现遵循先入先出的原则,现存人的数据要先取出;两个变量font,rear表示前后端rear=-1,font=-1表示队列中0个元素思路分析:ii)将尾指针往后移&... 查看详情

数据结构之队列(代码片段)

队列队列是一个有序列表,可以用数组或者链表来实现遵循先入先出的原则,现存人的数据要先取出;两个变量font,rear表示前后端rear=-1,font=-1表示队列中0个元素思路分析:ii)将尾指针往后移&... 查看详情

leetcode题解——数据结构之栈和队列(代码片段)

1.用栈实现队列2.用队列实现栈3.最小值栈4.用栈实现括号匹配5.数组中元素与下一个比它大的元素之间的距离6.循环数组中比当前元素大的下一个元素1.用栈实现队列232.ImplementQueueusingStacks(Easy)栈的顺序为后进先出,而队列的顺序... 查看详情