首页 > 其他分享 >数据结构 ——— C语言实现链式队列

数据结构 ——— C语言实现链式队列

时间:2024-10-25 14:18:42浏览次数:9  
标签:pq 队列 NULL C语言 Queue phead 链式 数据结构 节点

目录

队列的概念以及示意图

数组队列和链式队列

链式队列的结构 

实现链式队列的准备工作

实现链式队列

1. 初始化

2. 打印队列的所有数据

3. 数据入队列(尾插)

4. 数据出队列(头删)

5. 访问队头的数据

6. 访问队尾的数据

7. 队列数据总个数

8. 判断队列是否为空

9. 释放队列的所有节点

10. 以队列逻辑遍历所有数据

Queue.h 的所有代码 

Queue.c 的所有代码


队列的概念以及示意图

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

入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

队列结构示意图:


数组队列和链式队列

数组队列:

把首元素当作队头,尾元素当作队尾
那么出队列就需要删除首元素,在把后面的元素向前挪动,降低了效率
且当空间不够时需要扩容,就存在异地扩容的问题,也会降低效率

链式队列:

把头节点当作队头,尾节点当作队尾
那么出队列只需要将指向头节点的指针指向下一个节点,在释放头节点即可,效率为O(1)
入队列只需要在尾节点尾插即可,所以选择实现链式队列


链式队列的结构 

// 数据类型
typedef int QDataType;

// 链式队列每个节点的结构
typedef struct QueueNode
{
	struct QueueNode* next; //指向下一个节点的指针
	
	QDataType data; //当前节点的数据
}QNode;

// 链式队列的结构
typedef struct Queue
{
	QNode* phead; //指向队头节点的指针

	QNode* ptail; //指向队尾节点的指针

	int size; //队列的总数据个数
}Queue;

实现链式队列的准备工作

创建3个文件

test.c 文件:用来测试增删查改功能是否完善

Queue.h 文件:用来声明动态顺序表的结构以及声明增删查改函数 

Queue.c 文件:用来实现动态顺序表的增删查改及功能函数


实现链式队列

1. 初始化

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

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

2. 打印队列的所有数据

void QueuePrint(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;

	printf("head<-");
	while (cur != NULL)
	{
		printf("%d<-", cur->data);

		cur = cur->next;
	}
	printf("tail\n\n");
}

3. 数据入队列(尾插)

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	// 开辟节点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));

	// 判断是否开辟成功
	if (newnode == NULL)
	{
		assert(pq->ptail == NULL);
		return;
	}

	newnode->data = x;
	newnode->next = NULL;

	if (pq->phead == NULL)
	{
		// 双重判断,更保险
		assert(pq->ptail);

		pq->phead = newnode;
		pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

分两种情况判断:

当队列没有节点时:直接将指向头尾节点的指针指向新节点
当队列有节点时:pq->ptail 就是指向最后一个节点的指针,那么 pq->ptail->next 就是节点的 next,直接指向新节点,再将 pq->ptail指向新节点
数据成功入队列,最后 size 自增1

测试代码:


4. 数据出队列(头删)

void QueuePop(Queue* pq)
{
	assert(pq);

	// 队列没有数据时
	if (pq->phead == NULL)
	{
		printf("无数据可出队列\n");
		return;
	}

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

	pq->size--;
}

分三种情况判断:

队列没有节点时:直接报错并返回
队列只有一个节点时:释放头节点,并将指向头尾节点的指针都置空
队列有多个节点时:保存下一个节点,再释放头节点,最后将指向头节点的指针指向保存的下一个节点
数据成功出队列,最后 size 自减1

测试代码:


5. 访问队头的数据

QDataType QueueFront(Queue* pq)
{
	assert(pq);

	// 队列没有数据时
	if (pq->phead == NULL)
	{
		printf("无数据可访问\n");
		return;
	}

	return pq->phead->data;
}

6. 访问队尾的数据

QDataType QueueBack(Queue* pq)
{
	assert(pq);

	// 队列没有数据时
	if (pq->ptail == NULL)
	{
		printf("无数据可访问\n");
		return;
	}

	return pq->ptail->data;
}

7. 队列数据总个数

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

	return pq->size;
}

8. 判断队列是否为空

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return (pq->phead == NULL) && (pq->ptail == NULL);
}

9. 释放队列的所有节点

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

	QNode* cur = pq->phead;

	while (cur != NULL)
	{
		QNode* next = cur->next;

		free(cur);

		cur = next;
	}

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

10. 以队列逻辑遍历所有数据

Queue q;
QueueInit(&q);

for (int i = 1; i <= 5; i++)
{
	QueuePush(&q, i);
}

printf("head<-");
while (!QueueEmpty(&q))
{
	// 访问当前队头数据
	printf("%d<-", QueueFront(&q));

	// 移除当前队头数据
	QueuePop(&q);
}
printf("tail\n\n");

测试代码:


Queue.h 的所有代码 

#include<stdio.h>
#include<string.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* phead; //指向队头节点的指针

	QNode* ptail; //指向队尾节点的指针

	int size; //队列的总数据个数
}Queue;


// 初始化
void QueueInit(Queue* pq);

// 打印
void QueuePrint(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);

Queue.c 的所有代码 

#include"Queue.h"

// 初始化
void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

// 打印
void QueuePrint(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;

	printf("head<-");
	while (cur != NULL)
	{
		printf("%d<-", cur->data);

		cur = cur->next;
	}
	printf("tail\n\n");
}

// 数据入队列(尾插)
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	// 开辟节点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));

	// 判断是否开辟成功
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->data = x;
	newnode->next = NULL;

	if (pq->phead == NULL)
	{
		// 双重判断,更保险
		assert(pq->ptail == NULL);

		pq->phead = newnode;
		pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

// 数据出队列(头删)
void QueuePop(Queue* pq)
{
	assert(pq);

	// 队列没有数据时
	if (pq->phead == NULL)
	{
		printf("无数据可出队列\n");
		return;
	}

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

	pq->size--;
}

// 访问队头的数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);

	// 队列没有数据时
	if (pq->phead == NULL)
	{
		printf("无数据可访问\n");
		return -1;
	}

	return pq->phead->data;
}

// 访问队尾的数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);

	// 队列没有数据时
	if (pq->ptail == NULL)
	{
		printf("无数据可访问\n");
		return -1;
	}

	return pq->ptail->data;
}

// 队列数据总个数
int	QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

// 是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return (pq->phead == NULL) && (pq->ptail == NULL);
}

标签:pq,队列,NULL,C语言,Queue,phead,链式,数据结构,节点
From: https://blog.csdn.net/weixin_55341642/article/details/143161145

相关文章

  • 数据结构 ——— C语言实现数组栈
    目录栈的概念以及示意图链式栈和数组栈链式栈:数组栈:数组栈的结构实现数组栈的准备工作实现数组栈初始化数组栈入栈(尾插)出栈(尾删)访问栈顶数据判断栈是否为空栈数据的总数访问栈的所有数据释放栈Stack.h的所有代码Stack.c的所有代码栈的概念以及示意图栈......
  • 数据结构有哪些
    数据结构分类涉及多方面,主要包括:1、线性结构、2、树形结构、3、图形结构、4、集合结构、5、文件结构。在这些种类中,线性结构是最基本、也是最为广泛使用的一种,它包括数组、链表、栈和队列等数据结构,通过线性的方式组织数据元素。以数组为例,它以连续的内存空间顺序存储数据,通过索引......
  • 基础数据结构(1)
    单链表与双链表的用处单链表单链表的存储:单链表的几种操作在表头插入一个数:先将这个数指向head指向的数,再将head指向这个数在表中的第k位后面插入一个数:先将这个数指向第k位指向的数,再将第k位指向这个数在表中删除一个数:让这个数直接指向下一个数的下一个数代码实现:/......
  • C语言中的作用域规则
    文章的开头段落:在C语言中,作用域规则是一个非常重要的部分,主要涉及变量和函数的可见性和生命周期。根据作用域的界限,可将C语言的作用域分为四种:文件作用域、函数作用域、块作用域和函数原型作用域。它们分别规定了变量或函数在程序中的可见区域和生存期长度。每种作用域各有其特......
  • 数据结构 - 树,三探之代码实现
    数据结构-树,三探之代码实现 本文介绍了使用数组和链表两种方式实现二叉树,包括初始化、节点操作(如获取、添加、删除)、以及遍历方法(前序、中序、后序、层次遍历)。测试代码已上传至代码库。 书接上回,今天和大家一起动手来自己实现树。相信通过前面的章节学习,大家已经明白树......
  • 编程语言有哪些分类?C语言和其他编程语言的区别?到底什么是高级语言,什么是低级语言?C
    编程语言有哪些分类?编程语言发展有打孔卡片、机器语言、汇编语言和高级语言这几种形态。高级语言对于程序员更友好,发展的形态五花八门。从编程方式看,有命令式、函数式和逻辑式三种。命令式以常见的C/C++/Java/C#/Py......
  • 无限可能|为什么C语言如此强大?探索应用领域+职业方向
    随着科技的不断进步和发展,计算机科学领域的就业前景也越来越广阔。而在这个快速发展的行业中,学习C语言将打开更多的职业大门。C语言作为一种强大的编程语言,在各个领域都有着广泛的应用,为互联网从业者提供了丰富多彩的职业选择。一、 ‌C语言的主要应用领域C语言具有良好的......
  • 数据结构 - 堆
    今天我们将学习新的数据结构-堆。01、定义堆是一种特殊的二叉树,并且满足以下两个特性:(1)堆是一棵完全二叉树;(2)堆中任意一个节点元素值都小于等于(或大于等于)左右子树中所有节点元素值;小根堆,根节点元素永远是最小值,即堆中每个节点元素值都小于等于左右子树中所有节点元素值;大根......
  • 如何在C语言中使用多线程
    首段:在C语言中使用多线程可以通过调用标准线程库(POSIXthreads,也叫做Pthreads)的相关API函数实现。Pthreads库中包括了创建线程、线程同步(锁与条件变量)、线程间通信、线程清理等多种功能的API,这些功能为开发者提供了并行处理能力,从而可以大大优化程序的性能。要在C语言中使用多......
  • C语言基础入门(小白)三种方法解决幽灵换行符问题
    首先,相信很多读者读到题目都会产生一个共同的疑问:什么是幽灵换行符???    幽灵换行符是指:在C语言中,当用scanf函数时,想要输入几个字符,比如:当输入‘a’之后按下回车键,运行自动结束,而不是等待输入第二个字符,第二个字符就像幽灵般消失了,这是为什么呢??    其实,原因......