[数据结构初阶】栈

各位读者老爷好,鼠鼠我好久没写博客了(太摆烂了),今天就基于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.mzph.cn/news/725963.shtml

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

相关文章

zabbix监控中间件服务

zabbix监控Nginx 自定义nginx访问量的监控项&#xff0c;首先要通过脚本将各种状态的值取出来&#xff0c;然后通过zabbix监控。找到自定义脚本上传到指定目录/etc/zabbix/script/ 在zbx-client客户端主机操作 #创建目录&#xff0c;然后将脚本上传到该目录mkdir /etc/zabbix/…

【JS】关于this的使用

this 前言一、this是什么&#xff1f;二、做什么&#xff1f;1.全局环境2.函数环境3.new实例对象4.apply、bind、call绑定4.1 apply()4.2 call()4.3 bind() 三、为什么用this&#xff1f;四、如何改变this&#xff1f;五、应用场景&#xff1f;总结 前言 痛点 经常写Vue项目&a…

C++单例模式、工厂模式

一、单例模式 (一) 什么是单例模式 1. 是什么&#xff1f; 在系统的整个生命周期内&#xff0c;一个类只允许存在一个实例。 2. 为什么&#xff1f; 两个原因&#xff1a; 节省资源。方便控制&#xff0c;在操作公共资源的场景时&#xff0c;避免了多个对象引起的复杂操作…

Unity的相机跟随和第三人称视角二

Unity的相机跟随和第三人称视角二 展示介绍第二种相机模式远离代码总结 展示 我录的视频上可能看不太出来&#xff0c;但是其实今天的这个方法比原来写的那个方法更简便并且死角更少一些。 这里我也做了两个人物偏移的视角&#xff0c;按Q是原来的两个相机模式切换&#xff0c…

论文阅读笔记 | MetaIQA: Deep Meta-learning for No-Reference Image Quality Assessment

文章目录 文章题目发表年限期刊/会议名称论文简要动机主要思想或方法架构实验结果 文章链接&#xff1a;https://doi.org/10.48550/arXiv.2004.05508 文章题目 MetaIQA: Deep Meta-learning for No-Reference Image Quality Assessment 发表年限 2020 期刊/会议名称 Publi…

Marin说PCB之POC电路layout设计仿真案例---01

最近娃哈哈饮料突然爆火&#xff0c;看新闻后才知道春晚的的时候宗老已经病的很严重了&#xff0c;现在也已经离我们而去了&#xff0c;宗老是一个值得我们尊敬爱戴的伟大企业家。于是乎小编我立马去他们的直播间买了一箱娃哈哈AD钙奶支持一下我们的国货。 中午午休的时候&…

Excel如何开启VBA进行二次开发

经常使用Excel做数据分析的朋友平时用的比较多的可能只是一些常用的内置函数或功能&#xff0c;比如求和函数、字符串分割函数、分类汇总、IF函数、VLOOKUP函数等。大多数人认为Excel强大是因为内置了大量的函数。其实&#xff0c;作为一名资深程序猿&#xff0c;个人认为&…

kafka消费端消息去重方案

背景 我们在日常工作中&#xff0c;消费kafka消息是一个最常见的操作&#xff0c;不过由于kafka队列中经常包含重复的消息&#xff0c;并且消息量巨大&#xff0c;所以我们消费端总是需要先把消息进行去重后在消费&#xff0c;以减少消费端的压力&#xff0c;那么日常中我们一…

redis 性能优化一

目录 前言 尾延迟 前言 说到redis 性能优化&#xff0c;优化的目的是什么&#xff1f;提高响应&#xff0c;减少延迟。就要关注两点&#xff0c;一是尾延迟&#xff0c;二是Redis 的基线性能。只有指标&#xff0c;我们的优化&#xff0c;才有意义&#xff0c;才能做监控以及…

玩一会小乌龟

滚滚长江东逝水&#xff0c;浪花淘尽英雄。 是非成败转头空。青山依旧在&#xff0c;几度夕阳红。 白发渔樵江渚上&#xff0c;惯看秋月春风。 一壶浊酒喜相逢。古今多少事&#xff0c;都付笑谈中。 画一个正方形 import turtle# 创建一个Turtle对象 t turtle.Turtle()# 循环…

【开发工具】Git模拟多人开发场景理解分支管理和远程仓库操作

我们来模拟一个多人多分支的开发场景。假设你有一个新的空白远程仓库,假设地址是 https://github.com/user/repo.git。 克隆远程仓库到本地 $ git clone https://github.com/user/repo.git这会在本地创建一个 repo 目录,并自动设置远程主机为 origin。 创建本地开发分支并推送…

学术论文GPT的源码解读与二次开发:从ChatPaper到gpt_academic

前言 本文的前两个部分最早是属于此旧文的《学术论文GPT的源码解读与微调&#xff1a;从ChatPaper到七月论文审稿GPT第1版》&#xff0c;但为了每一篇文章各自的内容更好的呈现&#xff0c;于是我今天做了以下三个改动 原来属于mamba第五部分的「Mamba近似工作之线性Transfor…

开源模型应用落地-工具使用篇-Spring AI(七)

一、前言 在AI大模型百花齐放的时代&#xff0c;很多人都对新兴技术充满了热情&#xff0c;都想尝试一下。但是&#xff0c;实际上要入门AI技术的门槛非常高。除了需要高端设备&#xff0c;还需要面临复杂的部署和安装过程&#xff0c;这让很多人望而却步。不过&#xff0c;随着…

Kap - macOS 开源录屏工具

文章目录 关于 Kap 关于 Kap Kap 是一个使用web技术的开源的屏幕录制工具 官网&#xff1a;https://getkap.cogithub : https://github.com/wulkano/Kap 目前只支持 macOS 12 以上&#xff0c;支持 Intel 和 Apple silicon 你可以前往官网&#xff0c;右上方下载 你也可以使…

案例介绍:信息抽取技术在汽车销售与分销策略中的应用与实践

一、引言 在当今竞争激烈的汽车制造业中&#xff0c;成功的销售策略、市场营销和分销网络的构建是确保品牌立足市场的关键。作为一名经验丰富的项目经理&#xff0c;我曾领导一个专注于汽车销售和分销的项目&#xff0c;该项目深入挖掘市场数据&#xff0c;运用先进的信息抽取…

EasyExcel3.1.1版本上传文件忽略列头大小写

1、背景 项目中使用easyExcel3.1.1版本实现上传下载功能&#xff0c;相关数据DTO以 ExcelProperty(value "dealer_gssn_id") 形式规定其每一列的名称&#xff0c;这样的话easyExcel会完全匹配对应的列名&#xff0c;即用户上传文件时&#xff0c;列名写成Dealer_…

利用websocket +定时器简易的实现一个网络聊天室

其实原理非常简单,就是客户端用户通过websoket来连接websocket服务端。然后服务端,收集每个用户发出的消息, 进而将每条用户的消息通过广播的形式推送到每个连接到服务端的客户端。从而实现用户的实时聊天。 // TODO : 我主要是讲一下实现思路。并未完善其功能。 1.后端 依赖 …

使用数据库实现增删改查

#include<myhead.h>//定义添加数据函数int do_add(sqlite3 *ppDb) {//1.准备sql语句,输入要添加的信息int add_numb; //工号char add_name[20]; //姓名char add_sex[10]; //性别double add_score; //工资printf("请输入要添加的工号:")…

恢复IDEA误删除的git提交,提交被删除,尝试恢复提交

​​​​​​ dgqDESKTOP-JRQ5NMD MINGW64 /f/IdeaProjects/workspace/spzx-parent ((8bb112e...)) $ git reflog 8bb112e (HEAD, origin/master, master) HEAD{0}: checkout: moving from master to 8bb112e5ac18dfe4bbd64adfd06363e46b609f21 8bb112e (HEAD, origin/master, …

微信小程序开发系列(二十一)·wxml语法·setData()修改数组类型数据(增加、修改、删除)

目录 1. 新增数组元素 方法一&#xff1a;push&#xff08;&#xff09; 方法二&#xff1a;concat() 方法三&#xff1a;ES6中的扩展运算符 ... 2. 修改数组元素 样式一&#xff1a;数字 样式二&#xff1a;元素 3. 删除数组元素 方法一&#xff1a;splice&#x…