返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言编程数据结构栈与队列的全面讲解示例教程
  • 631
分享到

C语言编程数据结构栈与队列的全面讲解示例教程

2024-04-02 19:04:59 631人浏览 独家记忆
摘要

目录一、栈的表示和实现1栈的概念和结构2栈的初始化3压栈(栈顶插入一个数据)4出栈(栈顶删除一个数据)5取栈顶元素6取栈顶元素7判断栈是否为空二、队列的表示和实现1队列的概念及结构2

一、栈的表示和实现

1栈的概念和结构

栈:一种特殊的线性表(逻辑上数据是连着放的),其只允许在固定的一端进行插入和删除操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵循后进先出的原则。
压栈:栈的插入操作叫作进栈/压线/入线,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。

在这里插入图片描述

(图片来自比特就业课)

先入后出就类似与你装手枪弹夹,你先放入的子弹会在弹夹底部,最后放入的子弹是在弹夹顶部,你开手枪是先打出弹夹顶端的子弹。
我们这里先定义一个栈的类型:


typedef int STDataType;//方便将来如果需要其他类型的数据,可以直接修改int的类型
typedef struct Stack
{
	STDataType*a;//栈底指针
	int top;//栈顶标号
	int capacity;//容量
}ST;

本文介绍以顺序表(数组)实现栈,由此,所谓的入栈压栈也不过是顺序表的尾插尾删,如果读者想以链表实现也是可以的,方法不唯一。

2栈的初始化

关于栈的初始化:我们这里以容量大小4的栈为例,刚开始因为栈里没有数据我们用top=0标记,需要注意的是:传过来的栈底指针不能为空,开辟空间时万一没有开辟成功返回一个空指针也要丢弃。


#include<assert.h>
#include<stdlib.h>//malloc函数头文件
void StackInit(ST*ps)//栈初始化
{
	assert(ps);//防止传过来的指针是空指针
	ps->a = (STDataType)malloc(sizeof(STDataType) * 4);//malloc开辟一块空间出来,由STDataType类型进行管理
	//malloc,及后文出现的realloc、free函数详情见笔者动态内存管理文章
	if (ps->a == NULL)
	{
		printf("realloc fail\n");
		exit(-1);//终止程序
	}
	ps->capacity=4;
	//这里以开辟4个数据容量大小的栈为例,你也可以写其他数字
	//如果压栈的时候内存不够,可以在后面提到的压栈函数里面进行扩容
	ps->top = 0;//刚开始栈里没有值时,用top=0标记,后续每放入一个top++
	//关于top详细用法请往下看1.3压栈部分
}

3压栈(栈顶插入一个数据)

在这里插入图片描述

如上图,我们现在开辟了一块空间,a和top都在栈顶,我们往里面入一个数据1

在这里插入图片描述

1进入栈之后,1就是栈顶元素了,那如果我想继续入栈,就是要在top的位置放入一个数据让新放入的数据成为新的栈顶元素,然后以此类推,每次入一个元素,top++,top不是表示栈顶元素位置,而是栈顶元素下一个位置。

在这里插入图片描述


#include<assert.h>//assert函数头文件
#include<stdlib.h>//realloc函数头文件
void StackPush(ST*ps, STDataType x)//栈顶插入数据(入栈)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		STDataType*tmp = realloc(ps->a, ps->capacity * 2 * sizeof(STDataType));
		//realloc是在原先开辟的空间上继续往后开辟一块空间,详细见笔者动态内存文章
		//扩容一般扩2倍
		if (tmp == NULL)//扩容失败(比如内存已经不够你再开辟空间了)会返回空指针
		{
			printf("realloc fail\n");
			exit(-1);//终止程序
		}
		else
		{
			ps->a = tmp;
			ps->capacity *= 2;
		}
	}
	ps->a[ps->top] = x;//a是一个指针,a[x]==*(a+x)
	ps->top++;
}

4出栈(栈顶删除一个数据)

出栈非常简单,我们以下图为例:

在这里插入图片描述

图中我们栈里已经存放了4个数据1、2、3、4,那我们现在要进行出栈操作,也就是删除4怎么办?直接top–即可

在这里插入图片描述

到这里肯定会有小伙伴有疑问,凭什么你top–一下就是出栈了,你4都没删除啊?解释如下:我们在1.3压栈部分就说过“top不是表示栈顶元素位置,而是栈顶元素下一个位置”,那现在我top在4这里,说明3才是真正的栈顶元素,4已经不在栈的管辖范围内了。


void StackPop(ST*ps)//栈顶删除数据(出栈)
{
	assert(ps);
	assert(ps->top > 0);//栈空了,调用Pop,直接中止程序报错
	ps->top--;
}

关于ps->top > 0,是这样的:假设你原先栈里没有有一个元素

在这里插入图片描述

你top–就是往下访问未知的领域了,这是非常严重的问题,所以我们这里用assert断言一下,防止栈里什么元素也没有让top标记到了未知领域。(再次强调一下:top不是表示栈顶元素位置,而是栈顶元素下一个位置)

5取栈顶元素


STDataType StackTop(ST*ps)//取栈顶数据
{
	assert(ps);
	assert(ps->top > 0);//栈空了,调StackTop,直接中止程序报错
	return ps->a[ps->top - 1];//top是栈顶元素下一个位置,top-1是栈顶元素
	//a[m]==*(a+m)
}

这里的思路和1.4几乎一样,也要防止栈里什么也没有的情况,然后正常返回栈顶元素即可,因为top是栈顶元素下一个位置,top-1是栈顶元素,然后你正常写就行,这里的
a[ps->top - 1]你也可以写成*(a+(ps->top - 1)),这个写法读者可参加笔者以前的指针文章,这里不再赘述。

6取栈顶元素


int StackSize(ST*ps)//栈的数据个数
{
	assert(ps);
	return ps->top;
}

这个就更简单了,直接返回top的值就可以了,为什么呢,我们看两张图即可
图一:

在这里插入图片描述

图二:

在这里插入图片描述

图一是压栈前,没有一个元素,top=0,图二是压栈后,top++,top=1。同样的以此类推,我们每加入1个元素,top++,每减去一个元素,top–。元素个数永远=top值,所以我们这里的函数直接返回top值即可。

7判断栈是否为空


int StackEmpty(ST*ps)//判断栈是不是空
{
	assert(ps);
	return ps->top == 0;
}

由1.6知,我们的top值是和栈里元素个数一样的,所以我们直接return ps->top == 0;即可,如果ps->top == 0这个表达式成立表达式值为1,反之为0。

二、队列的表示和实现

1队列的概念及结构

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

在这里插入图片描述

(图片来自比特就业课)

类似你走人工通道那样,你排在前面的,你先出去

2队列的实现

由于队列的队头出,队尾入,非常类似单链表的头删和尾插,我们这里介绍以单链表实现队列,如下图,现有3个节点的单链表:

在这里插入图片描述

比如现在,我要队头出一个,也就是把单链表节点第一个节点删掉(头删)

在这里插入图片描述

或者队尾入一个,也就是单链表的尾插

在这里插入图片描述

代码如下(示例):


typedef int QDataType;
typedef struct Queuenode
{
	struct QueueNode*next;
	QDataType data;
}QNode;//这里和单链表定义一样,有需要的可以看一下笔者之前的单链表文章
typedef struct Queue//定义一个结构体存储头节点地址和尾节点地址,方便后面头删和尾插
{
	QNode*head;
	QNode*tail;
}Queue;

3队列初始化

代码如下(示例):


void QueueInit(Queue*pq)队列初始化,pq是一个结构体指针
{
	assert(pq);//判断pq是否是空指针
	pq->head  = NULL;
	pq->tail = NULL;
}

4入队(队尾插入一个数据)


void QueuePush(Queue*pq, QDataType x)//入队(队尾入)
{
	assert(pq);
	QNode*newnode = (QNode*)malloc(sizeof(QNode));//开辟出一块空间给newnode
	if (newnode == NULL)//有可能剩余内存不够开辟空间,malloc开辟失败会返回空指针
	{
		printf("开辟空间失败\n");
		exit(-1);//退出程序
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->tail == NULL)//原先队列里没有任何数据,头和尾指针都指向NULL
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

关于下面if这段代码解释如下:


    if (pq->tail == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

原先队列里没有任何数据,头和尾指针都指向NULL

在这里插入图片描述

当你往队列里面放了一个数据,头和尾指针是指向新的数据(头和尾指针仍指向一个)

在这里插入图片描述

如果你想再加1个节点,你首先得把两节点连上,也就是pq->tail->next = newnode;(没接上之前tail还指向第一个节点),接上之后tail指针指向第二节点

在这里插入图片描述

5出队(队头删除一个数据)


void QueuePop(Queue*pq)//出队(队头出)
{
	assert(pq);
	assert(pq->head);//要出队,队里也要有数据可以出	
	//原队列只有1个数据
	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;
	}
}

关于出队,这里要分2种情况讨论:

1.原队列只有1个数据

在这里插入图片描述

只有一个数据的时候,头和尾指针都指向1节点,他们的next是空指针,所以我们以这个条件为判断。又因为要出队(队头删除一个数据),因为只有一个节点,也就是把这个节点给删掉,我们用free函数释放掉head指向的空间。释放完,那块空间已经还给内存了,这时你的头和尾指针就不能指向那块空间了,我们用空指针赋值。
2.原队列有多个数据

在这里插入图片描述

我们现在要出队(队头删除一个数据),也就是把1节点删掉,我们先创建一个变量next找到2节点位置,然后free掉1节点的空间

在这里插入图片描述

1节点空间free掉之后,把next(指向2节点)赋给head,让头指针指向2节点。

6取队头数据


QDataType QueueFront(Queue*pq)//取队头数据
{
	assert(pq);
	assert(pq->head);
	return pq->head->data;
}

7取队尾数据


QDataType QueueBack(Queue*pq)//取队尾数据
{
	assert(pq);
	assert(pq->head);
	return pq->tail->data;
}

8计算队列中数据个数


int QueueSize(Queue*pq)//队内数据个数
{
	assert(pq);
	int size = 0;
	QNode*cur = pq->head;
	while (cur)//cur!=NULL进行循环,NULL是遍历完tail之后出现的
	{
		size++;
		cur = cur->next;
	}
	return size;
}

9判断队列是否为空


bool QueueEmpty(Queue*pq)
{
	assert(pq);
	return pq->head == NULL;
}

这一般是作为一个辅助函数来使用,bool 就是用来判断真假的数据类型,如果表达式:pq->head == NULL成立返回true,反之返回false

10销毁队列


void QueueDestory(Queue*pq)//队列销毁
{
	assert(pq);
	QNode*cur = pq->head;
	while (cur)//cur!=NULL进行循环,NULL是遍历完tail之后出现的
	{
		QNode*next = cur->next;
		free(cur);//free是释放指向的空间,指针还是在的
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

在这里插入图片描述

如上图,我们创建一个变量cur并用head指针赋值。然后找到第二个节点,用cur->next标记,标记完我们释放掉cur(释放的第一个节点空间,指针还是在的),释放完,cur开始遍历第二个节点,也就是我们先前用next标记的空间。。。剩下的以此类推。cur!=NULL进行循环,NULL是遍历完tail之后出现的,当循环不进行说明已经遍历完了尾指针指向的节点,头和尾节点已经不需要了,我们用空指针赋值。

总结

本文介绍了栈和队列的相关原理及各个接口函数,内容较多,知识点量大,希望读者耐心学习,相信你一定会有所收获,祝读者学习愉快~更多关于C语言数据结构栈与队列的资料请关注编程网其它相关文章!

--结束END--

本文标题: C语言编程数据结构栈与队列的全面讲解示例教程

本文链接: https://lsjlt.com/news/155171.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C语言编程数据结构栈与队列的全面讲解示例教程
    目录一、栈的表示和实现1栈的概念和结构2栈的初始化3压栈(栈顶插入一个数据)4出栈(栈顶删除一个数据)5取栈顶元素6取栈顶元素7判断栈是否为空二、队列的表示和实现1队列的概念及结构2...
    99+
    2024-04-02
  • C语言编程数据结构的栈和队列
    目录栈数组实现标题全部代码Stack_array.cStack_array.h初始化数组栈满栈后扩容是否为空栈压栈和退栈链表实现stack_chain.hstack_chain.c整...
    99+
    2024-04-02
  • C语言示例代码讲解栈与队列
    目录栈栈的定义顺序栈顺序栈的定义顺序栈的初始化顺序栈的入栈顺序栈的出栈取顺序栈的栈顶元素链栈队列队列的定义队列的顺序表达与实现队列顺序存储结构假溢出循环队列循环队列的初始化循环队列的...
    99+
    2024-04-02
  • Go语言数据结构全面解析:队列和栈解读
    队列遵循先进先出原则,在go语言中可使用链表实现。栈遵循后进先出原则,可使用切片便捷创建。队列适用于需按序处理数据的场景,如打印任务队列或消息队列。栈适用于需倒序处理数据的场景,如函数调...
    99+
    2024-04-08
    队列 go语言
  • C语言编程C++柔性数组结构示例讲解
    目录绕指柔—柔性数组柔性数组的特点:第一个好处是:方便内存释放第二个好处是:这样有利于访问速度总结绕指柔—柔性数组 也许你从来没有听说过柔性数组(flexible array)这个概...
    99+
    2024-04-02
  • C++数据结构的栈与队列实例分析
    今天小编给大家分享一下C++数据结构的栈与队列实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1. 栈1.1 栈的概念...
    99+
    2023-06-30
  • C语言数据结构之栈与队列的相互实现
    目录一、用对列实现栈代码实现二、用栈实现队列代码实现一、用对列实现栈 题干要求: 细节分析:队列是先进先出; 要实现的栈是先进后出。 解题思路:假设:先用一个队列储存数据 N 个,...
    99+
    2024-04-02
  • TypeScript数据结构之队列结构Queue教程示例
    目录1. 认识队列结构2. 实现队列结构封装3. 实战一:最近的请求次数3.1 题目描述3.2 解一:队列4. 实战二:无法吃午餐的学生数量4.1 题目描述4.2 解一:队列5. 实...
    99+
    2023-02-05
    TypeScript 队列结构 TypeScript Queue
  • C语言数据结构单链表接口函数全面讲解教程
    目录前言一、链表的概念及结构1.概念二、链表的使用1.遍历整个链表2.尾插3.头插4.头删5.尾删6.任意位置插入数据7.任意位置删除数据后记前言 上一期数据结构专栏我们学习了顺序表...
    99+
    2024-04-02
  • c语言数据结构之栈和队列详解(Stack&Queue)
    目录简介栈一、栈的基本概念1、栈的定义2、栈的常见基本操作二、栈的顺序存储结构1、栈的顺序存储2、顺序栈的基本算法3、共享栈(两栈共享空间)三、栈的链式存储结构1、链栈2、链栈的基本...
    99+
    2024-04-02
  • Go语言数据结构探究:队列与栈的应用
    go 语言中,队列遵守先进先出 (fifo) 原则,使用标准库中的 list 包实现,常用于消息传递系统;栈遵守后进先出 (lifo) 原则,常用于函数调用跟踪和括号匹配,可以使用切片实...
    99+
    2024-04-08
    go语言 数据结构 标准库
  • C语言数据结构之栈与队列怎么相互实现
    本篇内容介绍了“C语言数据结构之栈与队列怎么相互实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、用对列实现栈题干要求:细节分析:队列是...
    99+
    2023-07-02
  • C语言编程简单却重要的数据结构顺序表全面讲解
    目录前言一、线性表定义二、顺序表实现1概念及结构2静态顺序表2.1实现顺序表接口,第一步要对顺序表进行初始化2.2对顺序表的增删查改的接口函数(以尾插为例)3动态顺序表3.1动态顺序...
    99+
    2024-04-02
  • C语言数据结构线性表教程示例详解
    目录线性表顺序表线性表 数据结构里我们时常看到什么什么表,线性表是最基本、最简单、也是最常用的一种数据结构,其他各种表的万恶之源就是这个线性表,他是个啥其实顾名思义: 一个线性表是n...
    99+
    2024-04-02
  • C语言数据结构进阶之栈和队列的实现
    目录栈的实现:一、栈的概念和性质二、栈的实现思路三、栈的相关变量内存布局图四、栈的初始化和销毁五、栈的接口实现:1.入栈2.出栈3.获取栈顶的数据4.获取栈的元素个数5.判断栈是否为...
    99+
    2024-04-02
  • C语言数据结构不挂科指南之栈&队列&数组详解
    目录学习目标栈基本概念栈的基本运算栈的顺序实现双栈栈的链接实现考试要点小结学习目标 自考重点、期末考试必过指南,这篇文章让你理解什么是栈、什么是队列、什么是数组 掌握栈、队列的顺序存...
    99+
    2024-04-02
  • C语言数据结构算法基础之循环队列示例
    目录说明示例代码1. 首先定义结构体:2. 定义各种算法:3. 测试:4. 最后的结果:说明 循环队列是一种先进先出的,首尾相连的队列。 大致的结构如下图: 用数组来抽象的表示一下...
    99+
    2024-04-02
  • C语言数据结构之队列的定义与实现
    目录一、队列的性质二、队列的结构三、代码实现头文件功能函数一、队列的性质 上次我们学习栈,了解到栈储存释放数据的方式是:先进后出 而队列与其相反,队列是:先进先出,后进后出。 二、队...
    99+
    2024-04-02
  • Go语言数据结构精解:掌握队列和栈的奥秘
    队列遵循 fifo 原则,提供 enqueue、dequeue 和 peek 操作;栈遵循 lifo 原则,提供 push、pop 和 peek 操作。队列用于任务队列,栈用于函数调用、...
    99+
    2024-04-08
    队列 数据结构 go语言
  • C语言数据结构与算法之队列的实现详解
    目录队列的概念及结构队列的实现Queue.hQueue.cTest.c队列的概念及结构 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FI...
    99+
    2022-11-13
    C语言数据结构 队列 C语言 队列实现 C语言 队列
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作