当前位置: 首页 > news >正文

C语言实现基础数据结构——栈

目录

栈的实现

数组栈

数组栈的实现

栈的初始化

栈的销毁

数据入栈

判断栈是否为空

数据出栈

获取栈顶元素

获取栈内数据个数

项目实现

栈的基础练习

有效的括号


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

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

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

📌

注意在栈中数据出栈的顺序不一定和入栈的顺序相同,当数据入栈后又出栈不影响下一个数据的入栈顺序

例如:

入栈1 2 3 4 5,若4和5是入栈又出栈的,则出栈的顺序应该是4 5 3 2 1

但是不会出现一种情况,即:

已经入栈的数据(未在入栈后立马出栈)不会出现在先入栈又立马出栈的数据的前方

例如:

入栈 1 2 3 4,若3和4是入栈又立马出栈,则不可能出现2和1在3和4的前面

2.若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
A 1, 4, 3, 2
B 2, 3, 4, 1
C 3, 1, 4, 2
D 3, 4, 2, 1
例如本题中,入栈顺序为1, 2, 3, 4,那么出栈顺序最基本的就是后入先出,即出栈顺序为4,3,2,1,如果入栈又立马出栈的是2,3,4,则有2,3,4,1,如果入栈又立马出栈的是1,则有1, 4, 3, 2,如果入栈又立马出栈的是3, 4,则有3, 4, 2, 1,但是不可能存在3, 1, 4, 2,因为1作为第一个入栈的,要么就是入栈又立马出栈,作为第一个出栈的,要么就是最后一个出栈的

栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为栈满足后进先出或者即进即出,不需要额外移动数据,并且数组在尾上插入数据的代价比较小。

数组栈

//静态数组栈
typedef int STDataType;
#define N 10
typedef struct Stack
{STDataType _a[N];// 数组大小固定int _top; // 栈顶
}ST;//动态数组栈
typedef int STDataType;
typedef struct Stack
{STDataType* _a;// 数组大小不固定int _top;       // 栈顶int _capacity;  // 容量 
}ST;

数组栈的实现

//主要实现以下功能//栈的初始化
void STInit(ST* st);
//栈的销毁
void STDestroy(ST* st);
//数据入栈
void STPush(ST* st, STDataType x);
//数据出栈
void STPop(ST* st);
//判断栈是否为空
bool STEmpty(ST* st);
//获取栈元素
STDataType STTop(ST* st);
//获取栈数据个数
int STSize(ST* st);

栈的初始化

在初始化过程中注意top的初始值设置为0代表栈内数据的下一个位置,因为初始化栈代表栈内没有元素,如果用0代表栈内数据的当前位置,那么需要考虑该元素从何而来,既然没有数据,说明当前栈顶指针指向没有数据的位置,而数组下标为0代表第一个元素的位置,没有元素就不可能有第一个元素的位置,所以此时下标为0代表下一个元素的位置,此时说明栈内没有元素,但是准备在下一个位置(第一个元素)的位置添加数据

//栈的初始化
void STInit(ST* st)
{//判断是否存在队列assert(st);//初始化队列st->data = NULL;st->top = 0;//栈顶指针指向存储数据的下一个位置,代表栈内无数据//st->top = -1;//栈顶指针指向存储数据的位置,代表栈内无数据st->capacity = 0;
}

栈的销毁

//栈的销毁
void STDestroy(ST* st)
{//确保有栈的存在assert(st);//销毁栈free(st->data);st->data = NULL;//top和capacity更改为无数据的位置st->top = st->capacity = 0;
}

数据入栈

//数据入栈
void STPush(ST* st, STDataType x)
{//确保有栈的存在assert(st);//向top位置增加数据,并使top向后移动//需要判断栈的容量大小if (st->top == st->capacity){//如果栈的空间为0,则开辟四个空间,如果栈容量不为0,则扩容原来容量的2倍int newCapacity = st->capacity == 0 ? 4 : st->capacity * 2;STDataType* tmp = (STDataType*)realloc(st->data, sizeof(STDataType) * newCapacity);assert(tmp);st->data = tmp;//注意更新容量大小st->capacity = newCapacity;}//数据压栈并改变topst->data[st->top++] = x;
}

判断栈是否为空

//判断栈是否为空
bool STEmpty(ST* st)
{//确保有栈的存在assert(st);//栈为空返回真,栈不为空返回假return st->top == 0;//判断表达式返回值只有1和0,如果为真返回1(true),如果为假返回0(false)
}

数据出栈

//数据出栈
void STPop(ST* st)
{//确保有栈的存在assert(st);//确保栈不会越界assert(!STEmpty(st));//直接移动top指针,“看不见即删除”st->top--;
}

获取栈顶元素

//获取栈顶元素
STDataType STTop(ST* st)
{//确保栈存在assert(st);//确保栈不为空assert(!STEmpty(st));//top为栈内数据的下一个位置,要获取当前位置的元素需要-1操作return st->data[st->top - 1];
}

获取栈内数据个数

//获取栈内数据个数
int STSize(ST* st)
{assert(st);return st->top;
}

项目实现

//头文件
#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int STDataType;
typedef struct stack
{STDataType* data;int top; //栈顶位置int capacity; //元素个数
}ST;//栈的初始化
void STInit(ST* st);
//栈的销毁
void STDestroy(ST* st);
//数据入栈
void STPush(ST* st, STDataType x);
//数据出栈
void STPop(ST* st);
//判断栈是否为空
bool STEmpty(ST* st);
//获取栈顶元素
STDataType STTop(ST* st);
//获取栈内数据个数
int STSize(ST* st);//实现文件
#include "stack.h"//栈的初始化
void STInit(ST* st)
{//判断是否存在队列assert(st);//初始化队列st->data = NULL;st->top = 0;//栈顶指针指向存储数据的下一个位置,代表栈内无数据//st->top = -1;//栈顶指针指向存储数据的位置,代表栈内无数据st->capacity = 0;
}//栈的销毁
void STDestroy(ST* st)
{//确保有栈的存在assert(st);//销毁栈free(st->data);st->data = NULL;//top和capacity更改为无数据的位置st->top = st->capacity = 0;
}//数据入栈
void STPush(ST* st, STDataType x)
{//确保有栈的存在assert(st);//向top位置增加数据,并使top向后移动//需要判断栈的容量大小if (st->top == st->capacity){//如果栈的空间为0,则开辟四个空间,如果栈容量不为0,则扩容原来容量的2倍int newCapacity = st->capacity == 0 ? 4 : st->capacity * 2;STDataType* tmp = (STDataType*)realloc(st->data, sizeof(STDataType) * newCapacity);assert(tmp);st->data = tmp;//注意更新容量大小st->capacity = newCapacity;}//数据压栈并改变topst->data[st->top++] = x;
}
//数据出栈
void STPop(ST* st)
{//确保有栈的存在assert(st);//确保栈不会越界assert(!STEmpty(st));//直接移动top指针,“看不见即删除”st->top--;
}
//判断栈是否为空
bool STEmpty(ST* st)
{//确保有栈的存在assert(st);//栈为空返回真,栈不为空返回假return st->top == 0;//判断表达式返回值只有1和0,如果为真返回1(true),如果为假返回0(false)
}
//获取栈顶元素
STDataType STTop(ST* st)
{//确保栈存在assert(st);//确保栈不为空assert(!STEmpty(st));//top为栈内数据的下一个位置,要获取当前位置的元素需要-1操作return st->data[st->top - 1];
}//获取栈内数据个数
int STSize(ST* st)
{assert(st);return st->top;
}//测试文件
#define _CRT_SECURE_NO_WARNINGS 1#include "stack.h"void test()
{ST st = { 0 };//初始化栈STInit(&st);//数据压栈STPush(&st, 1);STPush(&st, 2);STPush(&st, 3);STPush(&st, 4);STPush(&st, 5);//打印栈内数据while (!STEmpty(&st)){//打印栈顶数据printf("%d ", STTop(&st));//数据出栈以打印下一个数据STPop(&st);}//栈的销毁STDestroy(&st);
}int main()
{test();return 0;
}

栈的基础练习

有效的括号

题目链接:20. 有效的括号 - 力扣(LeetCode)

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。

思路解析:

本题可以使用栈来解决,当括号为左侧括号时,将括号存入栈内,当括号为右侧括号时,将该括号与栈顶的括号进行比较,比较一次进行一次出栈操作

📌

注意本题不一定要使用C语言来解决,因为C语言本身不支持泛型,需要单独先实现栈才能解题,本题使用C语言和栈的思路仅供参考

参考答案:

/** @lc app=leetcode.cn id=20 lang=c** [20] 有效的括号*/// @lc code=start
// 使用C语言和栈解决问题
// 栈的声明
typedef char STDataType;
typedef struct stack
{STDataType *data;int top;      // 栈顶位置int capacity; // 元素个数
} ST;// 栈的初始化
void STInit(ST *st);
// 栈的销毁
void STDestroy(ST *st);
// 数据入栈
void STPush(ST *st, STDataType x);
// 数据出栈
void STPop(ST *st);
// 判断栈是否为空
bool STEmpty(ST *st);
// 获取栈元素
STDataType STTop(ST *st);// 栈的实现
// 栈的初始化
void STInit(ST *st)
{// 判断是否存在队列assert(st);// 初始化队列st->data = NULL;st->top = 0; // 栈顶指针指向存储数据的下一个位置,代表栈内无数据// st->top = -1;//栈顶指针指向存储数据的位置,代表栈内无数据st->capacity = 0;
}// 栈的销毁
void STDestroy(ST *st)
{// 确保有栈的存在assert(st);// 销毁栈free(st->data);st->data = NULL;st->top = st->capacity = 0;
}// 数据入栈
void STPush(ST *st, STDataType x)
{// 确保有栈的存在assert(st);// 向top位置增加数据,并使top向后移动// 需要判断栈的容量大小if (st->top == st->capacity){// 如果栈的空间为0,则开辟四个空间,如果栈容量不为0,则扩容原来容量的2倍int newCapacity = st->capacity == 0 ? 4 : st->capacity * 2;STDataType *tmp = (STDataType *)realloc(st->data, sizeof(STDataType) * newCapacity);assert(tmp);st->data = tmp;st->capacity = newCapacity;}// 数据压栈并改变topst->data[st->top++] = x;
}
// 数据出栈
void STPop(ST *st)
{// 确保有栈的存在assert(st);// 确保栈不会越界assert(!STEmpty(st));// 直接移动top指针,“看不见即删除”st->top--;
}
// 判断栈是否为空
bool STEmpty(ST *st)
{// 确保有栈的存在assert(st);// 栈为空返回真,栈不为空返回假return st->top == 0; // 判断表达式返回值只有1和0,如果为真返回1(true),如果为假返回0(false)
}
// 获取栈元素
STDataType STTop(ST *st)
{// 确保栈存在assert(st);// 确保栈不为空assert(!STEmpty(st));// top为栈内数据的下一个位置,要获取当前位置的元素需要-1操作return st->data[st->top - 1];
}// 判断是否是有效括号
bool isValid(char *s)
{// 基本思路:左括号入栈,右括号时左括号出栈与右括号比较// 创建栈用于保存数据ST st = {0};// 初始化栈STInit(&st);// 数据入栈while (*s){if (*s == '(' || *s == '[' || *s == '{'){// 当是左括号时数据入栈STPush(&st, *s);}else{// 如果栈为空直接返回falseif (STEmpty(&st)){// 返回前释放空间,因为尽管没有数据,但是栈的空间已经开辟STDestroy(&st);return false;}// 当是右括号时数据出栈与右括号比较char top = STTop(&st);// 更新栈内元素STPop(&st);if ((*s == ')' && top != '(') || (*s == ']' && top != '[') || (*s == '}' && top != '{')){// 此处找不同,如果此处找相同,那么但凡出现一对满足的括号就会进入返回true//  返回之前销毁栈防止内存泄漏STDestroy(&st);return false;}}++s;}// 如果栈为空,则说明匹配完成,如果不为空说明栈内仍有括号没有匹配到,此时说明存在单独的括号bool ret = STEmpty(&st);// 返回之前销毁栈STDestroy(&st);return ret;
}
// @lc code=end

相关文章:

C语言实现基础数据结构——栈

目录 栈 栈的实现 数组栈 数组栈的实现 栈的初始化 栈的销毁 数据入栈 判断栈是否为空 数据出栈 获取栈顶元素 获取栈内数据个数 项目实现 栈的基础练习 有效的括号 栈 栈是一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的…...

船舶制造5G智能工厂数字孪生可视化平台,推进船舶行业数字化转型

船舶制造5G智能工厂数字孪生可视化平台&#xff0c;推进船舶行业数字化转型。随着数字化时代的到来&#xff0c;船舶行业正面临着前所未有的机遇与挑战。为了适应这一变革&#xff0c;船舶制造企业需要加快数字化转型的步伐&#xff0c;提高生产效率、降低成本并增强市场竞争力…...

【网络编程】okhttp深入理解

newCall 实际上是创建了一个 RealCall 有三个参数&#xff1a;OkHttpClient&#xff08;通用配置&#xff0c;超时时间等&#xff09; Request(Http请求所用到的条件&#xff0c;url等) 布尔变量forWebSocket&#xff08;webSocket是一种应用层的交互方式&#xff0c;可双向交互…...

大功率厚膜电阻器制造 – 优化性能?

通过优化工业大功率电阻器制造工艺&#xff0c;制造商可以提高电阻器的性能和可靠性、容差、额定电压、TCR、稳定性和额定功率。 在本文中&#xff0c;我们将介绍工业功率电阻器的制造过程。我们讨论了材料选择和生产技术及其对性能的潜在影响。 完美的电阻器 在其整个使用寿…...

ElasticStack安装(windows)

官网 : Elasticsearch 平台 — 大规模查找实时答案 | Elastic Elasticsearch Elastic Stack(一套技术栈) 包含了数据的整合 >提取 >存储 >使用&#xff0c;一整套! 各组件介绍: beats 套件:从各种不同类型的文件/应用中采集数据。比如:a,b,cd,e,aa,bb,ccLogstash:…...

gitlab的使用

前一篇文章我们已经知道Git人人都是中心&#xff0c;那他们怎么交互数据呢&#xff1f; • 使用GitHub或者码云等公共代码仓库 • 使用GitLab私有仓库 目录 一、安装配置gitlab 安装 初始化 这里初始化完成以后需要记住一个初始密码 查看状态 二、使用浏览器访问&#xf…...

基于springboot+vue的植物健康系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…...

Python爬虫实战入门:爬取360模拟翻译(仅实验)

文章目录 需求所需第三方库requests 实战教程打开网站抓包添加请求头等信息发送请求&#xff0c;解析数据修改翻译内容以及实现中英互译 完整代码 需求 目标网站&#xff1a;https://fanyi.so.com/# 要求&#xff1a;爬取360翻译数据包&#xff0c;实现翻译功能 所需第三方库 …...

微服务-微服务API网关Spring-clould-gateway实战

1. 需求背景 在微服务架构中&#xff0c;通常一个系统会被拆分为多个微服务&#xff0c;面对这么多微服务客户端应该如何去调用呢&#xff1f; 如果根据每个微服务的地址发起调用&#xff0c;存在如下问题&#xff1a; 1.客户端多次请求不同的微服务&#xff0c;会增加客户端…...

ECMAScript modules规范示例详解

ECMAScript modules&#xff08;简称 ES modules&#xff09;是JavaScript的标准模块系统。每个模块都是一个独立的JavaScript文件&#xff0c;可以在其中定义导出的变量、函数或类&#xff0c;并从其他模块中导入这些变量、函数或类。以下是ES modules规范的一些示例和详解&am…...

【OpenFeign常用配置】

OpenFeign常用配置 快速入门&#xff1a;1、引入依赖2、启用OpenFeign 实践1、引入依赖2、开启连接池功能3、模块划分4、日志5、重试 快速入门&#xff1a; OpenFeign是一个声明式的http客户端&#xff0c;是spring cloud在eureka公司开源的feign基础上改造而来。其作用及时基于…...

第2.1章 StarRocks表设计——概述

注&#xff1a;本篇文章阐述的是StarRocks-3.2版本的表设计相关内容。 建表是使用StarRocks非常重要的一环&#xff0c;规范化的表设计在某些场景下能使查询性能有数倍的提升。StarRocks的表设计涉及到的知识点主要包括数据表类型、数据分布&#xff08;分区分桶及排序键&#…...

WooCommerce商品采集与发布插件

如何采集商品或产品信息&#xff0c;并自动发布到Wordpress系统的WooCommerce商品&#xff1f; 推荐使用简数采集器&#xff0c;操作简单方便&#xff0c;且无缝衔接WooCommerce插件&#xff0c;快速完成商品的采集与发布。 简数采集器的智能自动生成采集规则和可视化操作功能…...

select滑动分页请求数据

需求背景 Antd 的 select 组件支滑动分页获取后端数据 实现滑动加载数据 定义变量 const allLoadedRef useRef<boolean>(true); // 是否触底 const [current, setCurrent] useState<number>(1); // 当前页 const [list, setList] useState([]); // 列表定义…...

【Go channel如何控制goroutine并发执行顺序?】

多个goroutine并发执行时&#xff0c;每一个goroutine抢到处理器的时间点不一致&#xff0c;gorouine的执行本身不能保证顺序。即代码中先写的gorouine并不能保证先执行 思路&#xff1a;使用channel进行通信通知&#xff0c;用channel去传递信息&#xff0c;从而控制并发执行…...

逆向分析Cobalt Strike安装后门

Cobalt Strike简介 Cobalt Strike是一款基于java的渗透测试神器&#xff0c;也是红队研究人员的主要武器之一&#xff0c;功能非常强大&#xff0c;非常适用于团队作战&#xff0c;Cobalt Strike集成了端口转发、服务扫描&#xff0c;自动化溢出&#xff0c;多模式端口监听&am…...

【嵌入式学习】QT-Day3-Qt基础

1> 思维导图 https://lingjun.life/wiki/EmbeddedNote/20QT 2> 完善登录界面 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后…...

【杭州游戏业:创业热土,政策先行】

在前面的文章中&#xff0c;我们探讨了上海、北京、广州、深圳等城市的游戏产业现状。现在&#xff0c;我们切换视角&#xff0c;来看看另一个游戏创业热土——杭州的发展情况 最近第19届亚运会在杭州举办&#xff0c;本次亚运会上&#xff0c;电子竞技首次获准列为正式比赛项…...

Python-pdfplumber读取PDF内容

文章目录 前言一、pdfplumber模块1.1 pdfplumber的特点1.2 pdfplumber.PDF类1.3pdfplumber.Page类 二 pdfplumber的使用2.1 加载PDF2.2 pdfplumber.PDF 类2.3 pdfplumber.Page 类2.4 读取PDF2.5 读取PDF文档信息2.6 查看总页数2.7 查看总页数读取第一页的宽度&#xff0c;页高等…...

js设计模式汇总

目录 前言: 单篇目录: 工厂模式 单例模式 发布订阅模式 观察者模式 中介者模式 建造者模式 解释器模式 依赖注入模式 享元模式 路由模式 计算属性模式 委托者模式 访问者模式 外观模式 备忘录模式 过滤器模式 模板方法模式 状态模式 桥接模式 原型模式 组…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

【Linux】自动化构建-Make/Makefile

前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具&#xff1a;make/makfile 1.背景 在一个工程中源文件不计其数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;mak…...

LangFlow技术架构分析

&#x1f527; LangFlow 的可视化技术栈 前端节点编辑器 底层框架&#xff1a;基于 &#xff08;一个现代化的 React 节点绘图库&#xff09; 功能&#xff1a; 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…...

归并排序:分治思想的高效排序

目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法&#xff0c;由约翰冯诺伊曼在1945年提出。其核心思想包括&#xff1a; 分割(Divide)&#xff1a;将待排序数组递归地分成两个子…...

【51单片机】4. 模块化编程与LCD1602Debug

1. 什么是模块化编程 传统编程会将所有函数放在main.c中&#xff0c;如果使用的模块多&#xff0c;一个文件内会有很多代码&#xff0c;不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里&#xff0c;在.h文件里提供外部可调用函数声明&#xff0c;其他.c文…...