栈:程序员必备的工具箱

news2025/1/18 18:29:18

在这里插入图片描述

栈的结构和基本操作

本篇博客会讲解栈。栈是一种线性的数据结构,它满足“后进先出”的特性。栈有两端,分别是栈顶和栈底。每次插入或者删除数据,都是在栈顶的方向进行的。画个图解释一下:假设上面是栈顶,下面是栈底。
在这里插入图片描述

插入数据时,就从上面(栈顶)放数据进去。比如插入1的操作如下图:(这种操作称作“入栈”,或者叫push)
在这里插入图片描述
再插入2:
在这里插入图片描述
再依次插入[3 4 5]。
在这里插入图片描述
删除数据时,也是删除栈顶的元素,这种操作称作“出栈”,或者叫pop。对上面的栈进行出栈操作:第一次会删除栈顶的5。
在这里插入图片描述
再出栈,会删除4:
在这里插入图片描述
以此类推。栈的操作还有:取栈顶元素,比如上图中的栈顶元素是3。再比如,判断栈是否为空,显然上图中栈非空;以及栈中元素个数,上图中栈里有3个元素。

以上就是栈这种数据结构的常见操作了。栈满足“后进先出”的特性,如果先依次插入[1 2 3 4 5],全部入栈后,再出栈,出栈顺序就是[5 4 3 2 1],后入栈的会先出栈,即“后进先出”,简称LIFO,即Last In First Out。

数组栈vs链式栈

栈这种数据结构应该如何实现呢?我们可以使用数组或者链表实现。如果大家对顺序表和链表不太熟,建议大家先阅读我关于顺序表和链表这两种数据结构的解读,再来继续学习。顺序表,单链表,链表。

我们有3个选择:使用数组(即顺序表)实现,使用单链表实现,以及使用双链表实现。

  1. 使用数组,为了利用顺序表尾插、尾删效率高的优点,我们入栈使用尾插,出栈使用尾删,效率不错。
  2. 使用单链表,为了利用单链表头插、头删效率高的优点,我们入栈使用头插,出栈使用头删,效率不错。
  3. 若使用双链表,咋玩都行,因为任意位置的插入删除效率都很高。

如果在单链表和双链表中选一个,我会选择单链表,毕竟少用一点指针,效率也不错。而如果在数组和单链表中选一种实现,我会选择数组,因为在效率差不多的情况下,我们可以比较一下它们之间的劣势,两害相权取其轻。顺序表的劣势在于它需要扩容,不过这不是什么大问题,因为如果采取每次扩2倍的话,扩容不会太频繁,空间浪费也不会太多(最多浪费一半)。而链表的劣势在于,其内存空间不是连续的,这导致缓存的利用率不是很高,如果需要频繁访问数据,效率会降低不少。所以,我选择实现数组栈。

结构的定义

先来定义结构。和顺序表类似,我们使用一个指针来维护动态开辟的数组,使用top记录栈顶元素(注意:如果把top初始化成0,top-1才是栈顶元素的下标,这点后面会讲解),使用capacity来记录当前栈的容量,方便容量不够时扩容。

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;      // 栈顶
	int capacity; // 容量
}Stack;

初始化

先来实现一个函数,对栈进行初始化,以下是函数的声明:

void StackInit(Stack* ps);

使用malloc,一开始开辟4个数据的空间。注意和顺序表类似,我们是在外面先创建好一个Stack结构体,再把结构体的地址传给函数,所以ps不可能为NULL,需要断言检查指针的有效性。

void StackInit(Stack* ps)
{
	assert(ps);

	// 开辟4个空间
	ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL)
	{
		perror("malloc fail");
		return;
	}

	// 初始化
	ps->top = 0;
	ps->capacity = 4;
}

入栈

入栈操作类似顺序表的尾插,这是函数的声明:

void StackPush(Stack* ps, STDataType data);

由于一开始我们把top初始化成0,第一次插入数据后,会在下标为0的位置插入数据,然后把top更新成1;第二次插入数据,会在下标为1的位置插入数据,然后把top更新成2……所以,每次的操作是,在下标为top的位置插入数据,然后top加1,此时top标识栈顶元素的下一个位置的下标。

注意:当top和capacity相等时,说明栈已满,需要使用realloc扩容。

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);

	// 检查容量
	if (ps->top == ps->capacity)
	{
		// 扩容2倍
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		// 扩容成功
		ps->a = tmp;
		ps->capacity *= 2;
	}

	// 数据压栈
	ps->a[ps->top++] = data;
}

出栈

出栈操作类似顺序表的尾删,以下是函数的声明:

void StackPop(Stack* ps);

出栈前要断言栈非空,然后让top减1即可。

void StackPop(Stack* ps)
{
	assert(ps);
	// 检查非空
	assert(!StackEmpty(ps));

	ps->top--;
}

取栈顶元素

下面实现一个函数来取栈顶元素,以下是函数声明:

STDataType StackTop(Stack* ps);

当栈非空的情况下,取下标为top-1的元素。

STDataType StackTop(Stack* ps)
{
	assert(ps);
	// 检查非空
	assert(!StackEmpty(ps));

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

获取元素个数

下面实现一个函数来获取栈中的元素个数。函数的声明如下:

int StackSize(Stack* ps);

top就是元素个数。

int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}

判空

下面实现一个函数来判断栈是否为空,以下是函数声明:

bool StackEmpty(Stack* ps);

当top为0的状态,栈为空。

bool StackEmpty(Stack* ps)
{
	assert(ps);

	return ps->top == 0;
}

销毁

最后销毁栈,函数的声明如下:

void StackDestroy(Stack* ps);

释放掉空间即可。

void StackDestroy(Stack* ps)
{
	assert(ps);

	// 释放空间
	free(ps->a);
	ps->top = 0;
	ps->capacity = 0;
}

总结

  1. 栈是一种“后进先出”的线性的数据结构,每次操作都在一端(即栈顶)进行。
  2. 栈建议使用数组来实现,因为顺序表的尾插、尾删效率高,且缓存命中率较高。

感谢大家的阅读!

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

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

相关文章

linux ioctl 理解

背景 传统的操作系统可以分成两层,用户层和内核层。内核代码处理敏感资源同时在不同应用程序中间提供了安全且可信的隔离,出于此,操作系统要阻止用户态的程序直接访问内核资源。用户空间的程序通常发出一个给内核的请求,该请求称为…

基于vite4+pinia2模仿chatgpt移动端聊天模板Vue3MobileGPT

运用vite4.x构建mobile端仿chatgpt聊天实例Vue3-mobileGPT vue3-mobilegpt 基于 vite4vue3pinia2vue-routervant 等技术开发移动端仿ChatGPT智能聊天项目模板。支持lightdark两种主题,搭配vue3组件库Vant,界面简洁美观。 就前几天OpenAI就推出了IOS版Cha…

从 Vue Devtools 调用 WebStorm 打开文件

从 Vue Devtools 调用 WebStorm 打开文件 Vue Devtools 有一个功能, 可以直接在查看组件时, 直接打开对应的文件, 但默认是使用 VSCode 打开, 本文介绍如何使用 WebStorm 打开文件. 修改 vue.config.js: const openInEditor require("launch-editor-middleware");…

外包工作6年,聊一下感想.....

我不知道当年怎么想的,能在一个外包公司一干就是6年,后来终于跳出来了,现在的公司虽然不是什么大厂吧,但至少是个正经的互联网企业,待遇也不错。其实很多地方的朋友都有提到外包公司的一些弊端。 我个人的建议是&#…

linux下安装google谷歌浏览器

前言 记录下linux下安装谷歌浏览器全过程。 一、下载安装包 https://www.google.cn/intl/zh-CN/chrome/ 访问谷歌浏览器,拉到最下面 点击其他平台,选择linux 然后下载下来 下载完成后得到一个安装包 二、安装步骤 2.1.上传到linux服务器&#x…

chatgpt赋能Python-python_calu

Python Calu:Python程序员不可或缺的计算工具 作为一名有10年Python编程经验的工程师,我一直在使用Python编写各种程序,其中不可或缺的就是Python Calu。在下面,我将向您介绍Python Calu的特点及其在Python编程中的重要性。 什么…

uniapp内使用 mescroll

前言 在使用uniapp开发项目的过程中,在很多场景里都需要下拉刷新和上拉加载,而 mescroll.js 则是一个非常精致的下拉刷新和上拉加载 js 框架。 官网地址:mescroll 介绍 mescroll.js 是在 H5端 运行的下拉刷新和上拉加载插件,时…

如何使用 Xshell 连接 Linux 服务器

目录 🌳搭建 Linux 环境 🌱Linux 环境的搭建方式 ☘️购买云服务器 🌳使用Xshell远程登陆到Linux服务器 🌱下载安装Xshell ☘️查看Linux主机ip 🍀使用Xshell登录主机 💧方法1 💧方法2…

宝塔面板搭建网站教程:Linux下使用宝塔一键搭建网站,内网穿透发布公网上线

文章目录 前言1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4. 固定http地址5. 配置二级子域名6. 创建一个测试页面 转载自cpolar内网穿透的文章:使用宝塔面板快速搭建网站,并内网穿透实现公网远程访问 前言 宝塔面板作为简单好用的服务器运维管理面板&…

VScode必备插件大全

为了高效率的工作,我们在使用vScode的是时候,经常会用到一些好用的插件;现在就个人喜欢的插件分享给大家,欢迎点赞加收藏; 1、JavaScript(ES6) code snippets ES6 语法智能提示及快速输入,不仅仅支持 .js…

2009.03-2022.06华证ESG季度评级(季度)

2009.03-2022.06华证ESG评级(季度) 1、时间:2009.03-2022.06.15 2、来源:整理自Wind 3、指标:华证ESG(只有综合评级,无细分评级数据) 4、样本数量:A股4800多家公司 …

由浅入深Dubbo核心源码剖析高阶配置运用

目录 1 不同配置覆盖关系2 属性配置优先级3 重试与容错处理机制4 多版本控制5 本地存根调用6 负载均衡机制7 服务降级运用8 并发与连接控制 1 不同配置覆盖关系 Dubbo高阶配置运用 关于配置参看官方文档:https://dubbo.apache.org/zh/docsv2.7/user/configuration/ …

chatgpt赋能Python-python_bobo

Python Bobo:轻量级Web框架 Python是一个强大的编程语言,被广泛用于web应用程序和数据科学。用Python构建web应用程序的其中一条途径是使用框架。它们提供了一些实用的功能,如路由、模板、数据库集成等等。 Python中许多框架都很强大&#x…

Keil Debug 串口调试技巧

Keil Debug 串口调试技巧 效果 debug窗口效果 虚拟串口效果 debug窗口实现方法 第一步:配置参数 更改对应的bebug窗口参数 两边的 Dialog DLL 更改为:DARMSTM.DLL两边的 Parameter (这里的根据单片机型号更改)更改为&#xff…

chatgpt赋能Python-python_char

Python Char:了解 Python 字符的基础知识 Python是一种广泛使用的编程语言,因其易于学习、语法简单且适用于不同的应用场景而备受欢迎。在Python中,字符是一种重要的数据类型,也是值得深入学习的主题之一。本文将介绍Python字符的…

基于 Docker 搭建 ownCloud 个人云盘

本文源码:https://github.com/chen2438/chenhaotian.top/tree/main/source/_posts/linux-app/owncloud.md 在我的博客上查看:https://chenhaotian.top/2022/09/07/linux-app/owncloud/ 基于 Docker 搭建 ownCloud 个人云盘 官方文档 机翻气息贯穿全文…

【JUC基础】09. LockSupport

1、什么是LockSupport LockSupport是一个线程阻塞工具,可以在线程任意位置让线程阻塞。线程操作阻塞的方式其实还有Thread.suspend()和Object.wait()。而LockSupport与suspend()相比,弥补了由于resume()方法而导致线程被挂起(类似死锁&#x…

chatgpt赋能Python-python_bin目录

了解Python中的Bin目录 如果你是Python编程的初学者,可能会对Python中的Bin目录感到困惑。本文将为你介绍Bin目录的作用,以及为什么它对于Python编程人员来说是如此重要。 什么是Bin目录? Bin目录是Python安装时创建的一个目录&#xff0c…

Linux Yum指令

目录 一.yum 二.使用yum下载软件指令 1.安装指令: 2.查找在yum中所下载软件的指令: 3.卸载指令: 一.yum yum是Linux的一个指令,其实它的作用相当于一个商店,我们可以在商店里面买我们需要的东西。 我们将yum称为包…