[数据结构初阶】栈

news2025/1/19 20:25:27

各位读者老爷好,鼠鼠我好久没写博客了(太摆烂了),今天就基于C语言浅介绍一下数据结构里面的栈,希望对你有所帮助吧。

目录

1.栈的概念及结构

2.栈的实现

2.1定义栈

2.2.初始化栈 

2.3.入栈

2.4.出栈

2.5.获取栈顶元素

2.6.获取栈中有效元素个数

2.7.检查栈是否为空,如果为空返回真,如果不为空返回假

2.8.销毁栈 

3.栈小应用

3.1.Stack.h

3.2.Stack.c 

3.3.test.c 

4.小知识

5.ending


1.栈的概念及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入、删除和访问元素操作(ps:我们之前介绍的顺序表和链表都可以在任意位置插入、删除和访问)。进行数据插入、删除和访问操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

这里有几个概念需要理解,将栈比喻成手枪弹夹十分合适:
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶,弹夹出入弹口就像栈顶。

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

后进先出的原则:栈中的数据元素的插入、删除和访问均要遵循这个原则。栈中的数据元素就像子弹,先进入弹夹的子弹会后射出,后进入弹夹的子弹会先射出,栈中数据元素在栈中的操作就像弹夹的子弹一般。

举个列子解释栈中元素遵守的后进先出原则如图:

2.栈的实现

栈的实现一般可以使用数组或者链表实现,实现出来的栈只要满足数据结构对栈的定义即可。相对而言数组的结构实现更优一些,因为数组在尾上插删数据的代价比较小。所以下面我们实现一下以数组尾部为栈顶的数组栈。

而定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈。

我们栈主要实现以下功能:

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);

// 检测栈是否为空,如果为空返回真,如果不为空返回假 
bool StackEmpty(Stack* ps);

// 销毁栈 
void StackDestroy(Stack* ps);

2.1定义栈

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}Stack;

方便后续代码的维护,我们先不妨将int重命名成STDataType。由于实现动态生长的栈我们需要一个STDataType*类型指针a维护以后动态申请的空间(用来存放需存储的数据元素的)。用top指向栈顶元素的下一个元素(可以理解成元素个数)。用capacity记录以后动态图申请空间的大小。将a、top和capacity用结构体包起来并重命名成Stack。画下图方便理解:

2.2.初始化栈 

//初始化栈
void StackInit(Stack*ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

断言防止传入的Stack变量地址为空。不妨将a初始化为NULL,所以易知capacity初始化为0合适,由于将top设计成指向栈顶元素下一个元素(或者理解成元素个数) ,所以top也初始化为0。

2.3.入栈

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

	//扩容
	if (ps->top == ps->capacity)
	{
		int newcapacity = (ps->capacity == 0) ? 4 : (ps->capacity * 2);
		STDataType* tmp = realloc(ps->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail:");
			return;
		}
		ps->capacity = newcapacity;
		ps->a = tmp;
	}

	ps->a[ps->top] = data;
	ps->top++;
}

断言防止传入的Stack变量地址为空(这点以下如有相同不再赘述)。 当top和capacity相等时说明动态申请的空间不足以支持元素入栈,调用扩容函数(扩容了记得更新capacity),将元素在数组尾部入栈,top加一即可。

2.4.出栈

//出栈
void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}

断言防止栈为空的时候仍然出栈。元素在数组尾部出栈将top减一即可。 

2.5.获取栈顶元素

//获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

断言防止栈为空时获取栈顶元素(栈为空时获取的栈顶元素不是有效元素)。根据设定,易知top指向栈顶元素的下一个元素,所以ps->a[ps->top-1]就是栈顶元素。

2.6.获取栈中有效元素个数

//获取栈中有效元素个数
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}

根据设定,我们知道top的含义之一就是有效元素个数,所以返回ps->top即可。 

2.7.检查栈是否为空,如果为空返回真,如果不为空返回假

//检测栈是否为空,如果为空返回真,不为空返回假
bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
}

实现这个功能的话返回ps->top==0能很好的形成逻辑自洽。当栈为空时,ps->top==0为真,返回真;当栈不为空时,ps->top==0为假,返回假。 

2.8.销毁栈 

//销毁栈
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

我们再回顾一下栈的想象图: 

对于栈的销毁来说,我们需要主动释放动态申请的内存,就是结构体Stack成员中指针a指向的空间(这块空间也是来存放需存放数据元素的)。所以free掉ps->a,再将ps->a置成NULL、将ps->top和ps->capacity置成0即可。

3.栈小应用

上面这么多代码说不定老爷们看到云里雾里,但是没关系,鼠鼠我写一个工程运用一下上面代码(该工程包含上面所有代码),有兴趣的老爷们可以将下面三个文件放到同一个工程下面玩玩,再参考上面鼠鼠的愚见所不定会明白点!

3.1.Stack.h

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


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);

// 检测栈是否为空,如果为空返回真,如果不为空返回假 
bool StackEmpty(Stack* ps);

// 销毁栈 
void StackDestroy(Stack* ps);

3.2.Stack.c 

#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"


//初始化栈
void StackInit(Stack*ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

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

	//扩容
	if (ps->top == ps->capacity)
	{
		int newcapacity = (ps->capacity == 0) ? 4 : (ps->capacity * 2);
		STDataType* tmp = realloc(ps->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail:");
			return;
		}
		ps->capacity = newcapacity;
		ps->a = tmp;
	}

	ps->a[ps->top] = data;
	ps->top++;
}

//出栈
void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}

//获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

//获取栈中有效元素个数
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}

//检测栈是否为空,如果为空返回真,不为空返回假
bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
}

//销毁栈
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

3.3.test.c 

#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
int main()
{
	Stack s;
	StackInit(&s);
	StackPush(&s, 1);
	StackPush(&s, 2);
	StackPush(&s, 3);
	StackPush(&s, 4);
	StackPush(&s, 5);
	printf("%d\n", StackSize(&s));
	StackPop(&s);
	printf("%d\n", StackSize(&s));
	StackPush(&s, 6);
	while (!StackEmpty(&s))
	{
		printf("%d ", StackTop(&s));
		StackPop(&s);
	}
	printf("\n");
	printf("%d", StackSize(&s));
	StackDestroy(&s);
	return 0;
}

运行结果如图可以看看: 

 刚开始栈顶入栈五个数据元素,所以第一个printf打印5;然后将数据元素5栈顶出栈,所以第二个printf打印4;再次入栈数据元素6,此时栈内数据元素从栈底到栈顶分别为:1、2、3、4、6。

那我们如何打印栈内所有数据元素呢?

其实写一个如图中的while循环即可,由于栈要严格按照它的规定去访问数据元素,所以访问的数据元素只能时栈顶的,想要访问栈顶数据元素的前一个数据元素就必须将栈顶数据元素出栈才能访问到栈顶数据元素前一个数据元素,所以访问一遍栈后栈也就空了。

根据分析,while循环打印出来的应该是6 4 3 2 1。后面的printf打印为0也证明栈已经空了。

4.小知识

应该有一些老爷们听说过"栈溢出"这个概念,但我们这篇博客介绍的栈不是"栈溢出"的那个栈。

咱们这篇博客讲的栈是数据结构这门学科的概念,是一种数据结构,这个“栈”不存在“栈溢出”的概念。

“栈溢出”讲的栈是操作系统或者编程语言这些学科的概念。我们知道内存会划分成各种区域,其中有一个区域为栈区,“栈溢出”这个栈就是一个内存区域。很多情况会导致栈溢出,比如一个递归程序,由于递归返回条件有问题,导致递归不断建立栈帧,使得栈区空间没了还在建立栈帧就导致“栈溢出”。

5.ending

老弟我也是小白,如果有错误恳请各位大佬指正啊,感谢各位佬阅读到这里了!

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

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

相关文章

小白跟做江科大51单片机之LCD1602滚动显示效果

1.查看原理图 图1 LCD1602接口 图2 LCD1602与STC的接口 2.编写代码 图3 时序结构 根据时序结构编写命令和写入数据代码 #include <REGX52.H> #include "Delay.h" sbit LCD1602_ENP2^7; sbit LCD1602_RSP2^6; sbit LCD1602_WRP2^5; #define LCD1602_lCD0 …

Dgraph 入门教程二《 快速开始》

1、Clound 云 云地址&#xff1a;Dgraph Cloud 登录Clound 云后&#xff0c;可以用云上的东西操作&#xff0c;可以用谷歌账号或者github账号登录。 启动云 &#xff08;1&#xff09;在云控制台&#xff0c;点击 Launch new backend. &#xff08;2&#xff09;选择计划&…

AIGC工具( 7个 )

人工智能技术有好的一方面&#xff0c;又不好的地方&#xff0c;要区别对待&#xff0c;吸取精华&#xff0c;去其糟粕。目前市场上有很多AI大模型&#xff0c;可以支持聊天&#xff0c;写文稿&#xff0c;创作等&#xff0c;部分可以生成图片&#xff0c;以下是7个很不错的免费…

YOLOSHOW - YOLOv5 / YOLOv7 / YOLOv8 / YOLOv9 基于 Pyside6 的图形化界面

YOLOSHOW 是一个基于 PySide6&#xff08;Qt for Python&#xff09;开发的图形化界面应用程序&#xff0c;主要用于集成和可视化YOLO系列&#xff08;包括但不限于YOLOv5、YOLOv7、YOLOv8、YOLOv9&#xff09;的目标检测模型。YOLOSHOW 提供了一个用户友好的交互界面&#xff…

一张图带你了解数据分析的完整流程

一个完整的数据分析流程&#xff0c;应该包括以下几个方面&#xff0c;建议收藏此图仔细阅读。 作为数据分析师&#xff0c;无论最初的职业定位方向是技术还是业务&#xff0c;最终发到一定阶段后都会承担数据管理的角色。因此&#xff0c;一个具有较高层次的数据分析师需要…

electron 程序与安装包图标放大与制作

原因 electron-builder 在打包时需要最小支持到256x256像素的icon图标。原有历史图标都太小了。需要尝试将图标放大。 工具 convertio.co/zh/ico-png/ 在线ico转png网站 https://github.com/upscayl/upscayl 图片放大工具 csdn下载 greenfish-icon-editor-pro.en.softonic.c…

分布式ID生成系统之雪花算法详解

在当今的云计算和微服务架构盛行的时代&#xff0c;分布式系统已成为软件开发的重要组成部分。随着系统规模的扩大和业务的复杂化&#xff0c;对数据一致性和唯一性的要求也越来越高&#xff0c;尤其是在全局唯一标识符&#xff08;ID&#xff09;的生成上。因此&#xff0c;分…

mongo和redis的数据备份和还原

redis 安装 Redis安装和基本使用&#xff08;windows版&#xff09; - 知乎 window环境下Redis7服务器的安装和运行_redis7 windows-CSDN博客 备份数据 Redis SAVE 命令用于创建当前数据库的备份。 该命令将在 redis 安装目录中创建dump.rdb文件 查询路径 CONFIG GET dir…

[备赛笔记]——5G大唐杯(5G考试等级考考试基础试题)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;Vir2021GKBS &#x1f43c;本文由…

0基础学习VR全景平台篇第142篇:VR直播下载流程

大家好&#xff0c;欢迎观看蛙色VR官方——后台使用系列课程&#xff01; 这期&#xff0c;我们将为大家介绍如何下载VR直播内容。 一.如何下载VR直播内容&#xff1f; 首先登录蛙色官网&#xff0c;点击作品管理&#xff1b; 进入作品管理界面后选择全景直播&#xff0c;找到…

常用MII接口详解

开放式系统互连 (OSI) 模型 七层开放系统互连 (OSI) 模型中&#xff0c;以太网层 位于最底部两层 - 物理层和数据链路层。 从百兆以太网接口开始 首先是百兆以太网规定的两种接口 介质无关接口 (MII) Media Independent Interface 介质相关接口 (MDI) Medium Depen…

[Redis]——缓存击穿和缓存穿透及解决方案(图解+代码+解释)

目录 一、缓存击穿&#xff08;热点Key问题&#xff09; 1.1 问题描述 1.2 解决方案及逻辑图 1.2.1 互斥锁 1.2.2 逻辑过期 二、缓存穿透 2.1 问题描述 2.2 解决方案逻辑图 2.2.1 缓存空对象 2.2.2 布隆过滤器 一、缓存击穿&#xff08;热点Key问题&#xff09; 个人理…

web学习笔记(二十六)

目录 1.JS执行队列 1.1JS是单线程 1.2Web Worker 1.3同步和异步 1.4JS执行机制 2.location对象 2.1什么是location对象 2.2url包含的信息 2.3location对象属性 2.4location对象的方法 3.navigator对象和history对象 3.1navigator对象 3.2history对象 1.JS执行队…

manjaro 安装 wps 教程

内核: Linux 6.6.16.2 wps-office版本&#xff1a; 11.10.11719-1 本文仅作为参考使用, 如果以上版本差别较大不建议参考 安装wps主体 yay -S wps-office 安装wps字体 &#xff08;如果下载未成功看下面的方法&#xff09; yay -S ttf-waps-fonts 安装wps中文语言 yay …

数据结构之时间复杂度和空间复杂度

目录 一.什么是数据结构&#xff1f; 二.什么是算法&#xff1f; 三.算法效率 1.如何衡量算法的好坏 2.算法的复杂度 四.时间复杂度 1.时间复杂度的概念 2.例题展示 五.空间复杂度 1.概念 2.注意事项 空间的销毁>归还对空间的使用权内存空间属于操作系统的进程 …

【工具相关】zentao用例管理平台部署实践

文章目录 一、备份还原1、数据备份1.1、前言1.2、版本备份1.3、数据备份 2、数据恢复2.1、版本恢复2.2、数据恢复 二、问题处理1、ERROR: SQLSTATE[HY000] [2002] Connection refused 一、备份还原 1、数据备份 1.1、前言 禅道系统从10.6版本以后&#xff0c;新增数据备份设…

element-ui radio 组件源码分享

今日简单分享 radio 组件的实现原理&#xff0c;主要从以下三个方面来分享&#xff1a; 1、radio 页面结构 2、radio 组件属性 3、radio 组件方法 一、radio 页面结构 1.1 页面结构如下&#xff1a; 二、radio 属性 2.1 value / v-model 属性&#xff0c;类型为 string / …

谷歌新作:AI 检测文件内容类型,5ms 即可完成 | 开源日报 No.192

google/magika Stars: 5.0k License: Apache-2.0 magika 是一个利用深度学习来检测文件内容类型的工具。 使用自定义、高度优化的 Keras 模型&#xff0c;仅约 1MB 大小&#xff0c;在单个 CPU 上能够在毫秒内实现精确的文件识别。在超过 1M 文件和 100 种内容类型&#xff0…

供应链管理(SCM):界面设计全面扫盲,得供应链者得天下

大家伙&#xff0c;我是大千UI工场&#xff0c;专注UI分享和项目接单&#xff0c;本期带来供应链系统的设计分享&#xff0c;欢迎大家关注、互动交流。 一、什么是SCM SCM系统是供应链管理&#xff08;Supply Chain Management&#xff09;系统的缩写。供应链管理是指协调和管…

立式学习灯哪个牌子好?教你6个挑选窍门,甩掉坑货!

很多用户对立式学习灯的理解存在偏差&#xff0c;认为只要选择昂贵的、热度高的台灯就能万事大吉&#xff0c;实测不然!要知道&#xff0c;目前的市场上充斥着各类不专业立式学习灯&#xff0c;其中就包括不少所谓的网红品牌、跨界品牌&#xff0c;它们普遍通过造型精致、明星代…