深入浅出带你玩转栈与队列——【数据结构】

这篇具有很好参考价值的文章主要介绍了深入浅出带你玩转栈与队列——【数据结构】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

深入浅出带你玩转栈与队列——【数据结构】,数据结构

W...Y的主页 😊

代码仓库分享 💕


目录

1.栈

1.1栈的概念及结构

1.2栈的结构特征图 

​编辑 1.3栈的实现

1.3.1栈的初始化

1.3.2进栈

1.3.3出栈

1.3.4销毁内存

1.3.5判断栈是否为空

1.3.5栈底元素的读取

1.3.6栈中大小

1.4栈实现所有接口

2.队列

2.1队列的概念

2.2队列的结构 

 2.3队列的实现

2.3.1队列的接口总览

2.3.2队列的初始化

 2.3.3入队

2.3.4出队

2.3.5获取对头元素

2.3.6获取对尾元素

2.3.7判断是否为空

2.3.8队列销毁

3.拓展内容


 

在前几期的学习中,我们学习了顺序表与链表,今天我们将学习一种新的数据结构——栈与队列。而栈与队列实际上就是链表的一种变形产物,但肯定会有许多结构上的不同。

现在就让我们进入栈和队列,看看它们的特点!!!

1.栈

1.1栈的概念及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据也在栈顶。

1.2栈的结构特征图 

文字力量有时太过渺小,我们通过图解更清楚的了解栈的特点。

深入浅出带你玩转栈与队列——【数据结构】,数据结构

 1.3栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。

1.3.1栈的初始化

void STInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

这里我们使用数组作为结构,更好的适用于栈的特征。但是这又与顺序表中的不同,顺序表在刚进入就进行了开辟空间,而我们栈的初始化先全部设为0。

1.3.2进栈

在进栈时,我们会遇到栈内存满的情况,所以我们必须进行判断(top 有效内容与 capacity容量是否相等),如果相等我们就进行realloc进行扩容即可。然后将需要放入栈的内容存入堆区,有效内容top++即可完成。如果内容开始为0,我们使用三元表达式直接扩容为4。

void STPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->a[ps->top] = x;
		ps->top++;
	}
}

注意:malloc与realloc都可以使用,当给予realloc一个指针时,功能完全相同

1.3.3出栈

出栈非常简单,只需要将top--,不去访问其内容即可。

void STPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);

	--ps->top;
}

1.3.4销毁内存

销毁堆上内存也非常简单,只需free掉即可

void STDestroy(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

但是我们一定要用assert进行断言,防止传入空指针。

1.3.5判断栈是否为空

栈中是否插入内容,我们可以创建一个函数进行判断一下。返回值为bool类型,我们需要在头文件中加入#include<stdbool.h>才可以使用。

bool STEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}

1.3.5栈底元素的读取

在读取栈底元素时,我们需要检查传入栈的指针是否正确以及栈中是否为空,两个必须都为真即可进行下面操作。

STDataType STTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}

栈底元素就是top-1的内容,我们直接返回即可。

1.3.6栈中大小

当我们一直添加数据,需要了解栈中有多少个有效数据时,我们却束手无策,所以我们还需要一个能测出栈中大小函数。

int STSize(ST* ps)
{
	assert(ps);

	return ps->top;
}

1.4栈实现所有接口

我们需要了解的所有接口,可以放在头文件中:

// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef int STDataType;
#define N 10
typedef struct Stack
{
 STDataType _a[N];
 int _top; // 栈顶
}Stack;
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
 STDataType* _a;
 int _top; // 栈顶
 int _capacity; // 容量
}Stack;
// 初始化栈
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps); 

2.队列

2.1队列的概念

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

入队列:进行插入操作的一端称为队尾

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

2.2队列的结构 

深入浅出带你玩转栈与队列——【数据结构】,数据结构

 2.3队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数 组头上出数据,效率会比较低。

深入浅出带你玩转栈与队列——【数据结构】,数据结构

2.3.1队列的接口总览

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.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);
QDataType QueueFront(Que* pq);
QDataType QueueBack(Que* pq);
bool QueueEmpty(Que* pq);
int QueueSize(Que* pq);


在这里使用单链表,作为队列结构。队列特点就是先进先出,但是单链表比较适合头插头删,并不适合尾删,所以我们创建另一个结构体记录尾结点和头节点,这样也可以避免使用二级指针增加难度。

2.3.2队列的初始化

初始化我们先把所有内容设置成NULL或0即可

void QueueInit(Que* pq)
{
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

 2.3.3入队

入队其实就是链表中的尾插!!!

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

我们一定要记得将记录首尾的结构体进行赋值,分情况进行,不要忘记更新哦!!!

2.3.4出队

出队就是链表中的头删

void QueuePop(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(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--;
}

 我们一定要注意当删到只有一个内容节点时,要处理尾指针指向NULL。

2.3.5获取对头元素

只需要判断队列存指针正确且不为空,直接返回head指向的data即可。 QueueEmpty函数就是判断队列是否为空的函数,在后面有代码展示!!!

QDataType QueueFront(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return ps->head->data;
}

2.3.6获取对尾元素

尾元素获取与首元素基本相同。

QDataType QueueBack(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return ps->tail->data;
}

2.3.7判断是否为空

bool QueueEmpty(Que* pq)
{
	assert(pq);
	return ps->tail == NULL;
}

2.3.8队列销毁

一定不要忘记创建的存放头尾节点的指针结构体,也要将它们置空

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

3.拓展内容

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型 时可以就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。 深入浅出带你玩转栈与队列——【数据结构】,数据结构

深入浅出带你玩转栈与队列——【数据结构】,数据结构  

栈、队列、顺序表、链表都是相似但不同的数据结构,我们在学习时一定要了解其中的差异,这样才能在以后更好的应用!!!

以上就是本节全部内容,感谢大家耐心观看,你们的三连支持是博主最大创作动力。文章来源地址https://www.toymoban.com/news/detail-667740.html

到了这里,关于深入浅出带你玩转栈与队列——【数据结构】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 一文带你深入浅出Web的自动化测试工具Selenium【建议收藏】

    为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章中如果有记录错误,欢迎读者朋友们批评指正。 (博客的参考源码可以在我主页的资源里找到,如果在学习的

    2024年02月05日
    浏览(39)
  • 【深入浅出Docker原理及实战】「原理实战体系」零基础+全方位带你学习探索Docker容器开发实战指南(Docker-compose使用全解 一)

    Docker Compose是一款用于定义和运行复杂应用程序的Docker工具。在使用Docker容器的应用中,通常由多个容器组成。使用Docker Compose可以摆脱使用shell脚本来启动容器的繁琐过程。 Compose通过一个配置文件来管理多个Docker容器。在配置文件中,我们使用services来定义所有的容器。然后

    2024年01月17日
    浏览(55)
  • 带你玩转双链表

    相信经过前面的学习,大家已经了解的单链表的缺陷和用途,今天我们学习双链表,和以前不同,今天双链表的实现我们增加一点点的难度,但我相信这些难度对大家都没有问题。和之前单链表的实现,我们的数据类型是固定的,主函数中传什么我们的单链表结构体中就需要

    2024年02月13日
    浏览(25)
  • 带你玩转modbusTCP通信

    Modbus TCP是一种基于TCP/IP协议的Modbus通信协议,它是Modbus协议的一种变体,用于在以太网上进行通信。Modbus TCP协议是一种开放的通信协议,它支持多种编程语言和操作系统,并且可以在不同的硬件和软件平台上进行通信。 Modbus TCP协议使用标准的TCP/IP协议栈,通过以太网进行通

    2024年02月03日
    浏览(31)
  • 一文带你玩转ProtoBuf

    在网络通信和通用数据交换等应用场景中经常使用的技术是 JSON 或 XML,在微服务架构中通常使用另外一个数据交换的协议的工具ProtoBuf。 ProtoBuf也是我们做微服务开发,进行Go进阶实战中,必知必会的知道点。 今天就开始第一章内容:《一文带你玩转ProtoBuf》 你可能不知道

    2023年04月16日
    浏览(45)
  • 带你玩转单向链表(学习必备)

    本篇文章主要介绍数据结构中 单向链表 各种操作,适合有 C语言基础 的同学,文中描述和代码示例很详细,干货满满,感兴趣的小伙伴快来一起学习吧! ☀️大家好!我是新人博主朦胧的雨梦,希望大家多多关照和支持😝😝😝 🌖大家一起努力,共同成长,相信我们都会

    2024年02月02日
    浏览(33)
  • 带你玩转三子棋—【C语言】

    目录 前言: 1. 菜单的打印 2. game函数的实现 2.1 初始化棋盘 2.2 显示棋盘 2.3 玩家下棋 2.4 电脑下棋 2.5 判断输赢 2.6 判断棋盘是否满了 3. 全部代码 3.1 game.h 3.2  game.c 3.3 test.c 为了实现三子棋,首先我们应该将代码分模块编写,我们分为3个部分 1. test.c —测试游戏(主函数)2

    2024年02月04日
    浏览(29)
  • 一文带你玩转C语言文件操作

    作者主页:  paper jie的博客_CSDN博客 本文作者: 大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文录入于 《系统解析C语言》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将算法基础知识一网打尽,希望可以帮到读者们哦

    2024年02月17日
    浏览(48)
  • 【C语言】带你玩转库函数qsort

    君兮_的个人主页 勤时当勉励 岁月不待人 C/C++ 游戏开发 Hello,米娜桑们,这里是君兮_,之前更新的一直是比较基础和简单的内容,随着博主自己的水平的提升,今天给大家带来点不一样的东西,我们今天要讲的是库函数qsort的用法 废话不多说,咱们直接开始吧! 很多人可能是

    2024年02月16日
    浏览(26)
  • Python | 带你玩转Python的各种文件操作

    本篇文章主要介绍Python的各种文件操作,适合刚入门的小白或者对于文件操作基础不太牢固的同学,文中描述和代码示例很详细,看完即可掌握,感兴趣的小伙伴快来一起学习吧。 ☀️大家好!我是新人小白博主朦胧的雨梦,希望大家多多关照和支持😝😝😝 🌖大家一起努

    2023年04月11日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包