【数据结构】用C语言实现链队列(附完整运行代码)

news2024/11/19 14:34:18

🦄个人主页:修修修也

🎏所属专栏:数据结构

⚙️操作环境:Visual Studio 2022


一.了解项目功能

在本次项目中我们的目标是实现一个链队列:

链队列使用动态内存分配空间,可以用来存储任意数量的同类型数据.

队列结点(QNode)需要包含两个要素:数据域data,指针域next.

队列结点(QNode)逻辑结构图示如下:

   提供的功能有:

  1.   队列的初始化.
  2.   队列的入队.
  3.   队列的出队.
  4.   队列的长度.
  5.   队列的判空.
  6.   队列取队头元素.
  7.   队列取队尾元素.
  8.   队列的销毁

二.项目功能演示

要编写一个链队列项目,首先要明确我们想要达到的效果是什么样,下面我将用vs2022编译器来为大家演示一下链队列程序运行时的样子:

链队列的C语言实现


三.逐步实现项目功能模块及其逻辑详解

通过第二部分对项目功能的介绍,我们已经对链队列的功能有了大致的了解,虽然看似需要实现的功能很多,貌似一时间不知该如何下手,但我们可以分步分模块来分析这个项目的流程,最后再将各部分进行整合,所以大家不用担心,跟着我一步一步分析吧!


!!!注意,该部分的代码只是为了详细介绍某一部分的项目实现逻辑,故可能会删减一些与该部分不相关的代码以便大家理解,需要查看或拷贝完整详细代码的朋友可以移步本文第四部分。


1.实现链队列程序菜单

菜单部分的逻辑比较简单,就是利用C语言printf函数打印出这个菜单界面即可。但要注意菜单的标序要和后续switch...case语句的分支相应,以免导致后续执行语句错乱的问题.基础问题就不过多赘述了,代码如下:

该部分功能实现代码如下: 

void QMenu()
{
	printf("**********************************\n");
	printf("******请选择要进行的操作    ******\n");
	printf("******1.链队列入队          ******\n");
	printf("******2.链队列出队          ******\n");
	printf("******3.取队首元素          ******\n");
	printf("******4.取队尾元素          ******\n");
	printf("******5.队列判空            ******\n");
	printf("******6.查询当前队列长      ******\n");
	printf("******7.清空队列            ******\n");
	printf("******0.退出链队列程序      ******\n");
	printf("**********************************\n");
	printf("请选择:>");

}

2.实现链程序功能可循环使用

由于我们要实现  的功能可以反复使用的逻辑,且至少在一开始执行一次,因此我们选择do...while的循环语句来实现这一部分的逻辑.

该部分功能实现代码如下:

 int main()
    {
        Que Q;//创建队头尾指针结构体
        QueueInit(&Q);//初始化

        int swi = 0;//创建变量swi作为do...while循环的终止条件,以及switch语句的运行条件
        do          //使用do...while实现
        {

            QMenu();
            scanf("%d", &swi);

            switch (swi)
            {
            case 0:
                // 释放队列内存
                QueueDestroy(&Q);
            
                printf("您已退出程序:>\n");

                break;

            case 1:
                printf("请输入要入队的数据:>");
                QDatatype pushback_data = 0;
                scanf("%d", &pushback_data);

                QueuePush(&Q, pushback_data);

                printf("已成功入队:>\n");
                break;

            case 2:
                QueuePop(&Q);
                printf("出队成功:>\n");

                break;

            case 3:
                printf("队首元素为:");
                QDatatype e = QueueFront(&Q);
                printf("%d\n", e);
                break;

            case 4:
                printf("队尾元素为:");
                QDatatype n = QueueBack(&Q);
                printf("%d\n", n);
               
                break;

            case 5:
                if (!QueueEmpty(&Q))
                {
                    printf("当前队列不为空:>\n");
                }
                else
                {
                    printf("当前队列为空\n");
                }
                break;

            case 6:
               printf("当前队列长度为:");
               int size= QueueSize(&Q);
               printf("%d\n", size);
                break;

            case 7:
                QueueDestroy(&Q);
                QueueInit(&Q);
                printf("队列已清空:>\n");
                break;

            default:
                printf("输入错误,请重新输入\n");
                break;
            }
        } while (swi);

        return 0;
    }

3.创建链队列

创建链队列结点的结构体应该包括:存储数据的数据域data,以及存储下一个结点地址的指针域next.

图示如下:

因此我们创建QNode结构体类型时应由一个数据成员类型及一个指向该结构体的结构体指针组成.

然后队列特殊与单链表的点出现了:队列额外需要一个指向队头的队头指针,以及一个指向队尾的队尾指针,以及一个记录队长的整型size.

为了避免设计函数时要传好几个参数的情况,我们将这三个变量额外封装一个结构体,这样只要给函数传结构体变量就可以在函数内部访问这三个变量了.

综上,该部分代码如下:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

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

typedef int QDatatype;

typedef struct QueueNode//一个是结点结构
{
	struct QueueNode* next;
	QDatatype data;
}QNode;


typedef struct Queue//一个是队列整体结构
{
	QNode* head;
	QNode* tail;
	int size ;
}Que;

4.链队列的初始化.

回忆我们在链表部分对链表的初始化仅仅是将头指针置为NULL.而到了链队列这里,我们还多出两个需要处理的变量,一个是尾指针tail,一个是链队列长度size.

因此我们封装一个初始化函数将这3个变量一起初始化.

链队列结构初始化部分代码逻辑图示如下:

 综上,该部分代码如下:

void QueueInit(Que* pq)
{
	assert(pq);

	pq->head = pq->tail = NULL;
	pq->size = 0;

}

5.链队列的入队.

链队列在入队时思路如下:

  • 开辟新结点
  • 判断队列是否为空队列
  • 为空则将新结点"头插"进队列
  • 不为空则将新结点"尾插"进队列

综上,该部分代码如下:

void QueuePush(Que* pq, QDatatype x) //入队是尾插
{
    //创建新结点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
    //新结点赋值
	newnode->data = x;
	newnode->next = NULL;
    
    //当队列为空时,入队相当于头插
	if (pq->head == NULL)
	{
		assert(pq->tail == NULL);
        //head为空,tail不为空就出事了,即队头为空但是队尾不为空
		//将head和tail一起指向newnode
		pq->head = pq->tail = newnode;
		
	}
	else//否则就是尾插
	{
        //尾插直接将新结点链接在原来的队尾后面
		pq->tail->next = newnode;
        //然后更新队尾
		pq->tail = newnode;
	}
    
    //新结点插入成功后队列长度要+1
	pq->size++;

}

6.链队列的出队.

链队列在出队思路如下:

  • 判断队列是否为空队列
  • 如果是,抛出异常终止程序
  • 如果不是,则判断队列中是否仅剩一个结点
  • 如果只剩一个结点,释放该结点,然后将head和tail置为空
  • 如果不是只剩一个结点,那么使用一个指针记录下当前队头的下一个结点的位置
  • 释放当前队头结点,然后更新队头指针
  • 最后队列长度-1

综上,该部分代码如下:

void QueuePop(Que* pq)//出队是头删,删前先判空
{
	assert(pq);
	assert(!QueueEmpty(pq));//assert为假会终止程序

	QNode* cur = pq;

	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}

	pq->size--;

}

7.链队列的长度.

求链队列的长度,我们直接返回结构体中的size成员的值即可.

int QueueSize(Que* pq)
{
	assert(pq);

	return pq->size;

}

8.链队列的判空.

判断队列是否为空,我们可以返回(pq->size==0)表达式的值:

队列为空,则size=0,则pq->size==0表达式为真,函数返回true.

队列不为空,则size不等于0,则pq->size==0表达式为假,函数返回false.

综上,该部分代码如下: 

bool QueueEmpty(Que* pq)//判空!为空返回真!
{
	assert(pq);

	return pq->size==0;
}

9.链队列取队头元素.

取队头元素思路:

  • 判断队列是否为空
  • 为空抛出异常
  • 不为空返回队头指针指向的头节点的数据域

综上,该部分代码如下: 

QDatatype QueueFront(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;

}

10.链队列取队尾元素.

取队尾元素思路:

  • 判断队列是否为空
  • 为空抛出异常
  • 不为空返回队尾指针指向的尾节点的数据域

综上,该部分代码如下:

QDatatype QueueBack(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;

}

11.链队列的销毁

 链队列的销毁思路:

  • 从头遍历队列的元素,逐一释放链队列中的结点
  • 释放完将队头指针和队尾指针置为NULL,将队列长度size置为0.

 综上,该部分代码如下:

void QueueDestroy(Que* pq)
{
	assert(pq);

	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;

}

四.项目完整代码

我们将程序运行的代码分别在三个工程文件中编辑,完整代码如下:

test.c文件

#include"Queue.h"
	//因为链队列头指针是必须的
	//而在队尾插入则尾指针同样也是必须的
	//因此我们不妨设置两个指针,一个记录队头,一个记录队尾
	//为方便起见,多个数据我们把头指针和尾指针再封装一个结构体

	//单链表不设置尾指针的原因是它不能解决尾删问题

    int main()
    {
        Que Q;//初始化
        QueueInit(&Q);

        int swi = 0;//创建变量swi作为do...while循环的终止条件,以及switch语句的运行条件
        do          //使用do...while实现
        {

            QMenu();
            scanf("%d", &swi);

            switch (swi)
            {
            case 0:
                // 释放队列内存
                QueueDestroy(&Q);
            
                printf("您已退出程序:>\n");

                break;

            case 1:
                printf("请输入要入队的数据:>");
                QDatatype pushback_data = 0;
                scanf("%d", &pushback_data);

                QueuePush(&Q, pushback_data);

                printf("已成功入队:>\n");
                break;

            case 2:
                QueuePop(&Q);
                printf("出队成功:>\n");

                break;

            case 3:
                printf("队首元素为:");
                QDatatype e = QueueFront(&Q);
                printf("%d\n", e);
                break;

            case 4:
                printf("队尾元素为:");
                QDatatype n = QueueBack(&Q);
                printf("%d\n", n);
               
                break;

            case 5:
                if (!QueueEmpty(&Q))
                {
                    printf("当前队列不为空:>\n");
                }
                else
                {
                    printf("当前队列为空\n");
                }
                break;

            case 6:
               printf("当前队列长度为:");
               int size= QueueSize(&Q);
               printf("%d\n", size);
                break;

            case 7:
                QueueDestroy(&Q);
                QueueInit(&Q);
                printf("队列已清空:>\n");
                break;

            default:
                printf("输入错误,请重新输入\n");
                break;
            }
        } while (swi);

        return 0;
    }

 Queue.c 文件

#include"Queue.h"

void QueueInit(Que* pq)
{
	assert(pq);

	pq->head = pq->tail = NULL;
	pq->size = 0;

}
void QueueDestroy(Que* pq)
{
	assert(pq);

	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;

}

void QueuePush(Que* pq, QDatatype x) //入队是尾插
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->head == NULL)//头插
	{
		assert(pq->tail == NULL);//head为空,tail不为空就出事了,队头为空但是队尾不为空
		
		pq->head = pq->tail = newnode;
		
	}
	else//尾插
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

	pq->size++;

}


void QueuePop(Que* pq)//出队是头删
{
	assert(pq);
	assert(pq->head!=NULL);//***

	QNode* cur = pq;

	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}

	pq->size--;

}

int QueueSize(Que* pq)
{
	assert(pq);

	return pq->size;

}

bool QueueEmpty(Que* pq)//判空!为空返回真!
{
	assert(pq);

	return pq->size==0;
}

QDatatype QueueFront(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;

}
QDatatype QueueBack(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;

}

void QMenu()
{
	printf("**********************************\n");
	printf("******请选择要进行的操作    ******\n");
	printf("******1.链队列入队          ******\n");
	printf("******2.链队列出队          ******\n");
	printf("******3.取队首元素          ******\n");
	printf("******4.取队尾元素          ******\n");
	printf("******5.队列判空            ******\n");
	printf("******6.查询当前队列长      ******\n");
	printf("******7.清空队列            ******\n");
	printf("******0.退出链队列程序      ******\n");
	printf("**********************************\n");
	printf("请选择:>");

}

Queue.h文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

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

typedef int QDatatype;

typedef struct QueueNode//一个是结点结构
{
	struct QueueNode* next;
	QDatatype data;
}QNode;


typedef struct Queue//一个是队列整体结构
{
	QNode* head;
	QNode* tail;
	int size ;
}Que;

void QueueInit(Que* pq);
void QueueDestroy(Que* pq);

void QueuePush(Que* pq,QDatatype x);
void QueuePop(Que* pq);

int QueueSize(Que* pq);

bool QueueEmpty(Que* pq);

QDatatype QueueFront(Que* pq);
QDatatype QueueBack(Que* pq);

void QMenu();

结语

希望这篇链队列的实现详解能对大家有所帮助,欢迎大佬们留言或私信与我交流.

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

相关文章推荐

【数据结构】什么是线性表?

【数据结构】线性表的链式存储结构

【数据结构】C语言实现顺序表万字详解(附完整运行代码)

【数据结构】C语言实现单链表万字详解(附完整运行代码)

【数据结构】C语言实现带头双向循环链表万字详解(附完整运行代码)

【数据结构】什么是栈?

【数据结构】用C语言实现顺序栈(附完整运行代码)


数据结构栈与队列篇思维导图:

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

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

相关文章

2017年4月10日 Go生态洞察:开发者体验工作组介绍

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

我好像发现了车载测试面试成功的秘籍

在汽车行业中&#xff0c;车载测试工程师扮演着至关重要的角色。他们负责确保汽车的各种系统和功能在各种条件下都能正常运行&#xff0c;以确保车辆的安全性、可靠性和性能。如果你梦想成为一名车载测试工程师&#xff0c;那么你可能需要准备好回答一些关键的面试问题。在本文…

web:[ZJCTF 2019]NiZhuanSiWei1

题目 点进题目&#xff0c;网页显示如下&#xff0c;需要代码审计 $_GET["text"]和$_GET["file"]来获取传入的两个参数text和file。使用isset()函数来检查$text变量是否已设置并且不为null。如果设置了并且不为null&#xff0c;则执行下面的逻辑。在下面的…

C++实现十大排序算法

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

Proteus仿真--基于DS1302与数码管设计的可调电子钟

本文主要介绍基于51单片机的DS1302的可调式电子钟实验&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 其中数码管显示电子钟时间信息&#xff0c;按键用于调节时间&#xff0c;时间芯片选用DS1302芯片 仿真运行视频 Proteus仿真--基于DS1302与数码管设…

Debian 11.3 ARM64 安装中文语言包

文章目录 Debian 介绍1、执行命令2、语言选择3、修改设置 Debian 介绍 Debian是一种自由开源的操作系统&#xff0c;被广泛用于服务器、个人计算机和嵌入式设备。它是由全球志愿者组成的开发团队开发和维护的&#xff0c;以稳定性、安全性和自由性而闻名。 以下是一些关于Deb…

机器学习-线性模型·

线性模型是一类用于建模输入特征与输出之间线性关系的统计模型。这类模型的基本形式可以表示为&#xff1a; 其中&#xff1a; 是模型的输出&#xff08;目标变量&#xff09;。 是截距&#xff08;常数项&#xff0c;表示在所有输入特征都为零时的输出值&#xff09;。 是权重…

Yakit工具篇:WebFuzzer模块之热加载技术

简介 官方定义&#xff1a; 什么是热加载&#xff1f; 广义上来说&#xff0c;热加载是一种允许在不停止或重启应用程序的情况下&#xff0c;动态加载或更新特定组件或模块的功能。这种技术常用于开发过程中&#xff0c;提高开发效率和用户体验。 在Yakit 的Web Fuzzer中&…

PTA NeuDs_数据库题目

二.单选题 1.数据库应用程序的编写是基于数据库三级模式中的。 A.模式 B.外模式 C.内模式 D.逻辑模式 用户应用程序根据外模式进行数据操作&#xff0c;通过外模式一模式映射&#xff0c;定义和建立某个外模式与模式间的对应关系 2.对创建数据库模式一类的数据库对象的授权…

Python基础语法之判断语句

1.布尔类型和比较运算符 布尔类型&#xff1a;数字类型的一种。 比较运算符&#xff1a; > < > < ! 2.if语句基本格式 if 要判断的条件&#xff1a; 条件成立&#xff0c;即做~ 例子&#xff1a; 注意&#xff1a;格式上冒号和缩进 3.if else组合…

docker devicemapper: Error running DeleteDevice dm_task_run failed

docker 删除容器&#xff0c;遇到&#xff1a; devicemapper: Error running DeleteDevice dm_task_run failed 异常 [hadoophadoop02 ~]$ sudo docker rm 5ede1280f0bf Error response from daemon: container 5ede1280f0bf791e91d40038b15decd42e8923546ae578abd96e08114c76…

Linux 基础-常用的命令和搭建 Java 部署环境

文章目录 目录相关查看目录中的内容查看目录当前的完整路径切换目录 文件相关创建文件查看文件内容写文件vim 基础 创建删除创建目录 移动和复制移动(剪切粘贴)复制(复制粘贴) 搭建 Java 部署环境1. 安装 jdk2. 安装 tomcat1). 我们在自己电脑上下好 tomcat2). 从官网下载的 .z…

2023年【安全员-C证】考试试卷及安全员-C证试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全员-C证考试试卷是安全生产模拟考试一点通生成的&#xff0c;安全员-C证证模拟考试题库是根据安全员-C证最新版教材汇编出安全员-C证仿真模拟考试。2023年【安全员-C证】考试试卷及安全员-C证试题及解析 1、【多选…

【Java SE】 带你走近Java的抽象类与接口

&#x1f339;&#x1f339;&#x1f339;【JavaSE】专栏&#x1f339;&#x1f339;&#x1f339; &#x1f339;&#x1f339;&#x1f339;个人主页&#x1f339;&#x1f339;&#x1f339; &#x1f339;&#x1f339;&#x1f339;上一篇文章&#x1f339;&#x1f339;&…

【小沐学写作】原型设计工具汇总(Axure RP)

文章目录 1、简介2、Axure RP2.1 工具简介2.2 工具特点2.2.1 互动事件2.2.2 条件逻辑2.2.4 工作表格2.2.5 多状态容器2.2.6 数据驱动接口2.2.7 自适应视图2.2.8 流程图 2.3 工具安装2.3.1 安装2.3.2 运行 2.4 使用费用2.5 工具体验2.5.1 登陆框制作 3、其他3.1 Figma3.2 Adobe …

如何避免死锁

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

【设计模式-2.1】创建型——单例模式

说明&#xff1a;设计模式根据用途分为创建型、结构性和行为型。创建型模式主要用于描述如何创建对象&#xff0c;本文介绍创建型中的单例模式。 饿汉式单例 单例模式是比较常见的一种设计模式&#xff0c;旨在确保对象的唯一性&#xff0c;什么时候去使用这个对象都是同一个…

Vue19 列表过滤

直接上代码 以下代码使用了两种实现方式&#xff0c;监视属性和计算属性 当能用计算属性实现时&#xff0c;推荐使用计算属性 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>列表过滤</title><script type&q…

xadmin后台在每一行记录增加一个复制链接按钮

xadmin后台在每一行记录增加一个复制链接按钮 1、效果 点击复制后,自动把url链接复制到粘贴板,按Ctrl+v即可显示复制内容。 2、实现代码 adminx.py # 用户管理 class UserWhiteListAdmin(object):search_fields = [name, mobile] # 检索字段list_display

【Flutter】设置顶部状态栏的显示、隐藏、半透明灰色显示

【Flutter】设置顶部状态栏的显示、隐藏、半透明灰色显示 设置方法&#xff1a; // 这种模式不现实状态栏 SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); // 这种模式显示状态栏 SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); // 修…