c语言实现队列

news2025/4/21 11:52:21

文章目录

  • 前言
  • 一、队列的特征
  • 二、队列的实现
    • 1、队列的设计
    • 2、队列的初始化
    • 3、元素的入队和出队
    • 4、返回队头的数据和队尾的数据
    • 5、返回队列的长度
    • 6、队列的销毁
  • 三、循环队列
  • 四、队列和栈综合练习


前言

栈的特点是元素后进先出(Last In First Out),而对应的还有一种数据结构,该结构的特点是先进先出(First In First Out),即为队列。

一、队列的特征

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 的特点
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
在这里插入图片描述

二、队列的实现

1、队列的设计

队列可以使用数组或链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。因为队列需要尾插头删,当数组头删时,需要全部元素向前移动一位,时间复杂度为O(N),而使用链表来实现队列的话,头删和尾插元素的时间复杂度都是O(1)。

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int QDataType;
//队列的结点设计
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

//创建一个Queue结构体变量,就相当于创建了一个队列,该结构体变量中存的有该队列的头结点地址,尾结点地址
//所以当有该结构体变量的地址时,可以通过该地址改变队列的头结点地址和尾结点地址,即改变head指针和tail指针。
typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

2、队列的初始化

因为队列中需要有一个头指针用来记录队头结点的地址,一个尾指针用来记录队尾结点的地址。所以又创建了一个Queue结构体,该结构体中有两个QNode类型的成员变量,即Queue结构体中定义了两个结构体指针变量,一个用来存储队列的队头结点的地址,一个用来存储队尾结点的地址。
结构体Queue里面就只有两个QNode
类型的结构体指针,所以当想创建一个队列时,创建一个Queue类型的结构体变量s就好了,此时Queue类型的结构体变量s的成员head和tail里面存的都是随机的地址,所以需要初始化,则需要调用QueueInit()函数,并且将结构体变量s的地址传进去,因为只有传s的地址进去,函数里面才可以根据s的地址找到s的成员head和tail,然后才能改变head和tail里面存储的随机地址的值。
在这里插入图片描述

如果传的是s的话,那么函数的形参就设置为Queue pq,那么在函数中会临时创建一个Queue类型的结构体变量pq,然后将s的值拷贝到pq中,此时在函数中修改pq.head和pq.tail时,外面s的head和tail并不会改变,所以要传s的地址。
队列初始化就是通过s的地址 0x 1234找到s,然后改变s的成员变量head和tail的值为0x 0000。

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

在这里插入图片描述
在这里我们可以再看一下单链表的设计。
单链表中结构体的定义为

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

然后单链表在使用时,需要先创建一个SLTNode类型的结构体指针,然后再将该结构体指针的地址传入函数中。而函数中的形参都设计了二级指针来接收这个SLTNode类型的结构体指针的地址。这样设计是因为当单链表为空时,此时单链表的头结点指针ps就指向NULL,而当要给单链表插入元素时,如果只将ps传进去,那么在函数中形参只需设计为一级指针SLTNode*即可,但是这样设计的话,并不会改变ps的值,改变的只是函数中临时创建的和ps类型一致的一个指针变量的值。此时主函数的ps的值依旧为NULL,所以要向改变ps的值,只能将ps的地址传进去,然后函数中形参设计为二级指针,在函数中可以通过解引用ps的地址来找到ps,然后将ps的值设置为单链表结点的地址。
在这里插入图片描述
可以看到在前面的单链表中,我们用到了二级指针,但是为什么队列没有用二级指针呢?因为队列不止要有一个指针变量存储队头结点的地址,还需要一个指针变量来存储队尾结点的地址,而改变时需要改变存储队头结点的地址的指针head的值,也需要改变存储队尾结点的地址的指针tail的值。而改变这两个指针的值,就需要函数设计两个二级指针的形参,所以我们为了方便直接设计了一个结构体Queue,结构体的成员包含两个指针变量。
单链表的话就只需改变一个指向单链表头结点的指针的值,所以可以直接使用二级指针来实现。

3、元素的入队和出队

队列中元素在入队时我们要判断此时队列是否为空,如果队列为空的话,我们就需要将队头指针和队尾指针都指向入队的这个元素。如果队列不为空的话,此时就在队尾指针指向的队尾结点后插入这个新元素即可。然后将队尾指针指向这个新插入的结点,以保证队尾指针指向的总是队列的队尾结点。

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newNode->data = x;
	newNode->next = NULL;
	if (pq->head == NULL)
	{
		pq->head = newNode;
		pq->tail = newNode;
	}
	else
	{
		pq->tail->next = newNode;
		pq->tail = newNode;
	}
}

队列中元素在出队时需要先判断队列是否为空,如果队列为空,则出队失败。判断队列是否为空,就是判断队列的队头指针或队尾指针的值是否为NULL。

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	/*if (pq->head == NULL)
	{
		return true;
	}
	else
	{
		return false;
	}*/

	return pq->head == NULL;
}

然后在队列中有元素要出队时,先判断队列是否为空,不为空才可以让队列中队头指针指向的结点出队,并且当队列中只有一个结点时,要进行出队的话,这一个结点出队之后,虽然head指针指向了NULL,但是tail指针还指向了这个被释放空间的原来的队尾结点,所以此时tail为野指针,这就需要我们再判断当队列中只有一个结点要出队时,该结点完成出队后,需要将head指针和tail指针都置为NULL,以恢复初始时队列为空的状态。



void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	//当队列中只有一个结点时,该结点出队后队列就为空了
	//所以需要将队列的头指针和尾指针都置为NULL
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = NULL;
		//虽然按照下面的处理pq->head也会为NULL,
		//但tail指针还指向已经释放空间的最后一个结点的地址,所以此时tail为野指针,所以需要特别处理,将tail置为NULL
		pq->tail = NULL;
	}
	else
	{
		QNode* del = pq->head;
		pq->head = pq->head->next;
		free(del);
	}
	
}

4、返回队头的数据和队尾的数据

队列还需要提供返回队头结点数据和队尾结点数据的函数,返回队头结点数据就是先判断队列是否为空,不为空的话将队头指针所指向结点的数据返回即可。

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;
}

5、返回队列的长度

返回队列的长度可以通过遍历队列,每遍历一个结点,就将长度加1,最后将队列长度返回即可。不过这样求队列长度的时间复杂度为O(N)。

int QueueSize(Queue* pq)
{
	assert(pq);
	int size = 0;
	QNode* curr = pq->head;
	while (curr != NULL)
	{
		size++;
		curr = curr->next;
	}

	return size;
}

还可以在设计队列时,将Queue结构体中再加一个成员size,用来记录队列的长度,当有元素入队时pq->size++,当有元素出队时pq->size–,这样队列长度直接通过pq->size就可以得到。

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;  //用来记录队列长度
}Queue;

6、队列的销毁

队列的销毁就是先将队列的结点都一一销毁,然后将pq->head和pq->tail都指向NULL,此时队列中申请的结点占用的空间都被释放,而且队列回到了最初的状态。

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* curr = pq->head;
	while (curr)
	{
		QNode* next = curr->next;
		free(curr);
		curr = next;
	}
	pq->head = NULL;
	pq->tail = NULL;
	//在这里面将pq置为空没用,因为pq只是临时创建的一个Queue*类型的结构体指针
	//pq里面存的是Queue结构体变量的地址,在函数里面将pq置为NULL对外面没有影响。
	//只是让pq指向不了这个结构体变量了,但是这个结构体变量还存在,
}

三、循环队列

环形队列我们也可以通过一个例题来体会。
循环链表

四、队列和栈综合练习

在学习了队列和栈之后,我们可以用队列来实现栈,或用栈来实现队列,下面的链接为这两个问题的具体实现,可以点击链接进行学习。
用栈实现队列
用队列实现栈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/923722.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

十、pikachu之php反序列化

文章目录 1、php反序列化概述2、实战3、关于Magic function4、__wakeup()和destruct() 1、php反序列化概述 在理解这个漏洞前&#xff0c;首先搞清楚php中serialize()&#xff0c;unserialize()这两个函数。 &#xff08;1&#xff09;序列化serialize()&#xff1a;就是把一个…

一款打工人必备的电脑端自律软件!!冲鸭打工人!!

你&#xff01;有没有渴望进步&#xff01;&#xff01; 你&#xff01;有没有渴望变强&#xff01;&#xff01;&#xff01; 成为大佬&#xff01;&#xff01;&#xff01;超越巨佬&#xff01;&#xff01;&#xff01; 这就是一款为这样的你量身定做的程序&#xff1a;输入…

宝塔 杀死 java服务 netstat -tlnp | grep :7003 kill 2205698

7003 是端口 netstat -tlnp | grep :7003 kill 2205698

基于鸽群算法优化的BP神经网络(预测应用) - 附代码

基于鸽群算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于鸽群算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.鸽群优化BP神经网络2.1 BP神经网络参数设置2.2 鸽群算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

遥感影像的缨帽(K-T)变换Python实现

&#xff08;1&#xff09;介绍 缨帽变换&#xff08;Kirchhoff Transform&#xff0c;K-T变换&#xff09; 是一种在遥感图像处理中常用的技术&#xff0c;它可以有效地提取地物的空间特征和频谱信息。本文将对遥感缨帽变换的提出者、原理方法、公式、现在的发展、作用进行详…

Mybatis (3)-----分页的运用

目录 一、分页查询 二&#xff0c;特殊的字符处理 三、总结 前言&#xff1a;在我们上篇已经学的动态sql的基础上&#xff0c;今天继续讲解关于maybatis的分页&#xff0c;特殊的字符处理。希望这篇博客可以帮助到大家哦&#xff01; 一、分页查询 为什么要重写mybatis的分…

ATFX汇市:美国PMI数据不及预期,非农基数又遭下修,经济前景堪忧

环球汇市行情摘要—— 昨日&#xff0c;美元指数下跌0.22%&#xff0c;收盘在103.38点&#xff0c; 欧元升值0.17%&#xff0c;收盘价1.0864点&#xff1b; 日元升值0.7%&#xff0c;收盘价144.85点&#xff1b; 英镑贬值0.06%&#xff0c;收盘价1.2725点&#xff1b; 瑞郎…

基于Visual studio创建API项目

API&#xff08;英文全称&#xff1a;Application Programming Interface,中文&#xff1a;应用程序编程接口&#xff09; 为什么要 通过API接口可以与其他软件实现数据相互通信&#xff0c;API这项技术能够提高开发效率。 本文是基于vs2017 .net平台搭建API。希望可以帮助到学…

Python Opencv实践 - 图像直方图自适应均衡化

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/cat.jpg", cv.IMREAD_GRAYSCALE) print(img.shape)#整幅图像做普通的直方图均衡化 img_hist_equalized cv.equalizeHist(img)#图像直方图自适应均衡化 #1. 创…

一、MQ的基本概念

1、初识MQ MQ全称是Message Queue&#xff0c;消息队列&#xff0c;多用于系统之间进行异步通信。队列的概念数据结构中有详细介绍过&#xff0c;先进先出&#xff0c;消息队列就是存储消息的数据结构。 同步调用和异步调用两者之间的区别&#xff1a; 同步调用&#xff1a;发…

使用yapi生成漂亮接口文档

YApi-教程 1. 进入yapi 的菜单 2. 从微服务中导出swagger的json 从浏览器页面访问http://localhost:端口/服务/swagger-ui.html&#xff0c;然后打开浏览器的控制台&#xff0c;查看network&#xff0c;刷新下页面&#xff0c;找到XHR中的api-docs&#xff0c;然后查看res…

【代码】Java中的动态代理实战

文章目录 1. JDK 动态代理2、CGLIB 动态代理 动态代理允许你在运行时创建代理对象&#xff0c;来代替原始对象执行某些操作。这在AOP&#xff08;面向切面编程&#xff09;中非常有用&#xff0c;用于实现日志记录、性能监控、事务管理等功能。 Java提供了两种主要的动态代理实…

缓存的设计方式

问题情况&#xff1a; 当有大量的请求到内部系统时&#xff0c;若每一个请求都需要我们操作数据库&#xff0c;例如查询操作&#xff0c;那么对于那种数据基本不怎么变动的数据来说&#xff0c;每一次都去数据库里面查询&#xff0c;是很消耗我们的性能 尤其是对于在海量数据…

【0基础入门Python Web笔记】一、python 之基础语法、基础数据类型、复合数据类型及基本操作

一、python 之基础语法、基础数据类型、复合数据类型及基本操作 基础语法规则基础数据类型数字类型&#xff08;Numbers&#xff09;字符串类型&#xff08;String&#xff09;布尔类型&#xff08;Boolean&#xff09; 复合数据类型List&#xff08;列表&#xff09;Tuple&…

【C++代码】有序数组的平方,长度最小的子数组,螺旋矩阵 II--代码随想录

题目&#xff1a;有序数组的平方 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 题解 数组其实是有序的&#xff0c; 只不过负数平方之后可能成为最大数了。那么数组平方的最大值就在数组的…

shell 编写一个带有进度条的程序安装脚本

需求 使用 shell 写一个 软件安装脚本&#xff0c;带有进度条 示例 #!/bin/bash# 模拟软件安装的步骤列表 steps("解压文件" "安装依赖" "配置设置" "复制文件" "")# 计算总步骤数 total_steps${#steps[]}# 安装进度的初…

JVM——类加载与字节码技术—类加载器+运行期优化

5.类加载器 jdk的类加载器具有层级关系。 启动类加载器》扩展类加载器》应用程序类加载器》自定义类加载器 对应类加载器只会负责加载对应目录的类。 双亲委派上级机制 应用程序类加载器加载一个类之前会先查询上级加载器是否已经加载过了该类。然后再让上级询问上上级。都…

代码随想录算法训练营第四十四天|LeetCode 309,714

目录 LeetCode 309.最佳买卖股票时机含冷冻期 动态规划五步曲&#xff1a; 1.确定dp[i][j]的含义 2.找出递推公式 3.初始化dp数组 4.确定遍历方向 5.打印dp数组 LeetCode 714.买卖股票的最佳时机含手续费 动态规划五步曲&#xff1a; 1.确定dp[i]的含义 2.找出递推公式 3.初始…

项目构建工具:CMake的核心用法

Golang有go mod、Python有pip、Java有maven。但C语言没有这么好用的包管理工具。当然Conan大概可以算是一个&#xff0c;但其也有自身的局限性&#xff0c;使用起来并不简单。 这就导致我们在写C代码的时候&#xff0c;老是要把心思放在怎么构建项目上。比如有一个项目&#x…

重磅!亚马逊将于10月再次举行秋季会员大促!

今年亚马逊7月份的Prime Day大促出乎卖家意料&#xff0c;效果出奇之好&#xff0c;大促确实有提振销量和信心的奇效。 而在近期&#xff0c;亚马逊宣布&#xff0c;将在今年10月&#xff0c;继续为亚马逊Prime会员带来“Prime秋季会员大促”。 19个国家&#xff08;包括澳大…