数据结构C语言描述4(图文结合)--栈的实现,中序转后序表达式的实现
前言
- 这个专栏将会用纯C实现常用的数据结构和简单的算法;
- 有C基础即可跟着学习,代码均可运行;
- 准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言实现的原因之一;
- 欢迎收藏 + 关注,本人将会持续更新。
文章目录
- 什么是栈
- 栈的代码实现
- 数组栈
- 链式栈
- 栈的应用
- 求和存储数据的二进制
- 表达式求值详解
- 中缀表达式
- 后缀表达式
- 中缀转后缀
什么是栈
栈是一种运算受限的线性表,它限定只能在表的一端进行插入和删除操作。
栈0的结构特性是:后进先出LIFO ( Last In First Out)。栈又被称为后进先出表。
两个重要概念:
栈顶:允许插入和删除的一端称作栈顶;
栈底:不允许插入和删除的一端称作栈底。
数组栈结构如下:
其中:
- 栈底元素:a1 ;
- 栈顶元素:an ;
- 入栈顺序:a1, a2, …, an 依次入栈;
- 出栈顺序:an, an-1, …, a1,先入的后出 ,只能在栈顶进行插入和删除。
链式栈结构:
链式栈原理就是:头插入、头删除。
栈的代码实现
ADT抽象操作:
- 创建栈
- 入栈
- 出栈
- 获取栈顶元素
- 栈是否为空
- 栈元素个数
数组栈
解释:用数组模拟栈。
📦 栈封装
封装元素:
- 数据
- 数组当前能存储的最大大小(容量)
- 栈顶游标
typedef struct Stack {DataType* data;size_t maxSize; // int top;
}Stack;
💳 创建栈与栈初始化
这个步骤目标:创建栈,并且初始化栈变量;
栈顶初始化:-1,入栈:++top,出战:top--
Stack* create_stack()
{Stack* stack = (Stack*)calloc(1, sizeof(Stack));assert(stack);stack->top = -1; // 栈顶初始值为 -1return stack;
}
📌 入栈
操作:就是数组向后添加元素
注意:就是扩容操作,这里不够就**++10**
void push(Stack* stack, DataType data)
{assert(stack);// 扩容if (stack->top == stack->maxSize - 1) {DataType* temp = (DataType*)realloc(stack->data, (stack->maxSize + 10) * sizeof(DataType));assert(temp);stack->maxSize += 10;stack->data = temp;}stack->data[++stack->top] = data;
}
🔝 获取栈顶元素和出栈
获取栈顶:获取数组下标为top元素
出栈:top减1
DataType top(Stack* stack)
{assert(stack);return stack->data[stack->top];
}void pop(Stack* stack)
{assert(stack);stack->top--;
}
🚄 万金油函数
- 获取栈大小
- 栈是否为空
这两个操作,没什么难度,看代码即可;
bool empty(Stack* stack)
{assert(stack);return stack->top == -1;
}size_t size(Stack* stack)
{assert(stack);return stack->top + 1;
}
⚗️ 总代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int DataType;typedef struct Stack {DataType* data;size_t maxSize;int top;
}Stack;Stack* create_stack()
{Stack* stack = (Stack*)calloc(1, sizeof(Stack));assert(stack);stack->top = -1; // 栈顶初始值为 -1return stack;
}void push(Stack* stack, DataType data)
{assert(stack);// 扩容if (stack->top == stack->maxSize - 1) {DataType* temp = (DataType*)realloc(stack->data, (stack->maxSize + 10) * sizeof(DataType));assert(temp);stack->maxSize += 10;stack->data = temp;}stack->data[++stack->top] = data;
}DataType top(Stack* stack)
{assert(stack);return stack->data[stack->top];
}void pop(Stack* stack)
{assert(stack);stack->top--;
}bool empty(Stack* stack)
{assert(stack);return stack->top == -1;
}size_t size(Stack* stack)
{assert(stack);return stack->top + 1;
}int main()
{Stack* stack = create_stack();for (int i = 1; i <= 10; i++) {push(stack, i);}printf("size: %llu\n", size(stack));while (!empty(stack)) {printf("%d ", top(stack));pop(stack);}putchar('\n');printf("size: %llu\n", size(stack));return 0;
}
链式栈
🌇 实现方式:采用无头链表;
🌙 模拟:采用链表的头插和弹出头,来模拟入栈和出栈操作。
📦 栈封装
节点封装
- 存储元素、指针指向下一个节点
链表封装
- 采用在封装写法,创建一个指针,指向头节点,同时定义size,存储当前栈的大小,是这个在封装写法的核心。🤔 为什么说size是核心变量??,图示如下:
typedef int DataType;typedef struct Node {DataType data;struct Node* next;
}Node;typedef struct Stack {Node* headNode;int size;
}Stack;
🖍 封装创建节点和创建栈函数
Node* create_node(DataType data)
{Node* node = (Node*)calloc(1, sizeof(Node));assert(node);node->data = data;return node;
}Stack* create_stack()
{Stack* stack = (Stack*)calloc(1, sizeof(Stack));assert(stack);return stack;
}
📌 元素入栈
在封装写法的核心是变量size,当size==0
的时候,说明这个时候是链表为空,这个时候插入就是创建头节点,其他情况就是正常头插,这样引入变量也可以避免指针指向被修改的问题,不需要采用二级指针。
图:
// 头插
void push(Stack* stack, DataType data)
{assert(stack);Node* newNode = create_node(data);if (stack->size == 0) {stack->headNode = newNode;}else {newNode->next = stack->headNode;stack->headNode = newNode;}stack->size++;
}
🤕 出栈和获取栈顶元素
获取栈顶:就是获取头节点元素过程;
出战:删除头节点,要注意的就是栈为空的情况。
图示:
DataType top(Stack* stack)
{assert(stack);assert(stack->headNode);return stack->headNode->data;
}void pop(Stack* stack)
{assert(stack);if (stack->size == 0) {return;}else {Node* temp = stack->headNode;stack->headNode = stack->headNode->next;free(temp);temp = NULL;}stack->size--;}
💛 万金油函数
采用在封装写法对获取元素和判断是否为空,有着天然的优势,代码如下:
bool empty(Stack* stack)
{assert(stack);return stack->size == 0;
}int size(Stack* stack)
{assert(stack);return stack->size;
}
总代码:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>// 采用无头链表实现typedef int DataType;typedef struct Node {DataType data;struct Node* next;
}Node;typedef struct Stack {Node* headNode;int size;
}Stack;Node* create_node(DataType data)
{Node* node = (Node*)calloc(1, sizeof(Node));assert(node);node->data = data;return node;
}Stack* create_stack()
{Stack* stack = (Stack*)calloc(1, sizeof(Stack));assert(stack);return stack;
}// 头插
void push(Stack* stack, DataType data)
{assert(stack);Node* newNode = create_node(data);if (stack->size == 0) {stack->headNode = newNode;}else {newNode->next = stack->headNode;stack->headNode = newNode;}stack->size++;
}DataType top(Stack* stack)
{assert(stack);assert(stack->headNode);return stack->headNode->data;
}void pop(Stack* stack)
{assert(stack);if (stack->size == 0) {return;}else {Node* temp = stack->headNode;stack->headNode = stack->headNode->next;free(temp);temp = NULL;}stack->size--;}bool empty(Stack* stack)
{assert(stack);return stack->size == 0;
}int size(Stack* stack)
{assert(stack);return stack->size;
}int main()
{Stack* stack = create_stack();for (int i = 1; i <= 10; i++) {push(stack, i);}while (!empty(stack)) {printf("%d ", top(stack));pop(stack);}return 0;
}
栈的应用
其实在开发中用栈,要不用封装好容器,要么用数组模拟,不会像上面那种方式手写。
求和存储数据的二进制
要求:求一个数的二进制
// 一般栈开发中都有容器,直接调用,哪怕是C语言也是简单的模拟,如下
void test_stack()
{// 用栈存储并打印 666 的二进制int stack[1024] = { 0 }; // 数组模拟栈int top = -1; // 定义栈顶int num = 666;int t = num;while (t) {stack[++top] = ((t & 1) == 1) ? 1 : 0; // 入栈t >>= 1;}// 输出和弹出栈顶while (top != -1) {printf("%d", stack[top--]); // 获取栈顶与出栈}
}
表达式求值详解
中缀表达式
我们把平时所用的标准四则运算表达式,即“9+(3-1)×3+10÷2”叫做中缀表达式。因为所有的运算符号都在两数字的中间。
后缀表达式
后缀表达式也叫作逆波兰表达式,对于“9+(3-1)×3+10÷2”,如果要用后缀表示法应该是什么样子:“9 3 1-3*+10 2/+”,这样的表达式称为后缀表达式,叫后缀的原因在于所有的符号都是在要运算数字的后面出现。后缀表达式如何求解表达式的值?
- 遍历后缀表达式,如果遇到数字则直接入栈,如果遇到操作符,则弹出栈顶的两个元素,进行计算后将结果入栈。
- 最终,栈内剩余的元素就是整个表达式的计算结果。
中缀转后缀
操作流程
- 如果栈顶元素的优先级大于等于当前操作符,则先将栈顶元素弹出并输出到后缀表达式中,再将当前操作符压入栈中。
- 如果遇到了左括号,则直接将其压入栈中,如果遇到了右括号,则弹出栈中的元素,直到遇到了左括号为止,并将这些元素输出到后缀表达式中。
- 最后,将栈中剩余的元素依次弹出,并输出到后缀表达式中。
⏰ 代码实现
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>/*
* 中缀:a + b * (c + d) / e - f
* 后缀:a b + c d + * e / f -
*/#define MAX_SIZE 2014
char stack[MAX_SIZE]; // 定义栈
int top = -1;bool isdigits(char figure)
{return ((figure - '0' >= 0) && (figure - '0' <= 9));
}bool is_operator(char op)
{return op == '+' || op == '-' || op == '*' || op == '/';
}int pri_operator(char key)
{switch (key){case '+':case '-':return 1;case '*':case '/':return 2;}return 0;
}void midToLast(char* p, char* q)
{assert(p);assert(q);while (*p != '\0') {// 是数字 255+43if (isdigits(*p)) { // 是数字while (isdigits(*p) || *p == '.') {*q = *p;p++;q++;}// 加空格,输出好看好一点*q = ' ';q++;}else if (is_operator(*p)) { // 运算符if (top == -1) { // 栈空stack[++top] = *p;p++;}else if (pri_operator(stack[top]) >= pri_operator(*p)) { // 栈顶符号 优先即大于等于 当先运算符while (top != -1 && is_operator(stack[top])) { // 弹出*q = stack[top--]; q++;// 加空格,输出好看好一点*q = ' ';q++;}}else {stack[++top] = *p;p++;}}else if (*p == '(') {stack[++top] = *p;p++;}else if (*p == ')') {while (top != -1 && stack[top] != '(') {*q = stack[top--];q++;// 加空格,输出好看好一点*q = ' ';q++;}if (top != -1) { // 不是-1,就是左括号top--;}p++;}}// 剩余运算while (top != -1) {*q = stack[top--];q++;// 加空格,输出好看好一点*q = ' ';q++;}*q = '\0';
}int main()
{// 10 33 22 - 10 * + 12 4 / + 0.25 +char midExpression[MAX_SIZE] = { "10+(33-32)*10+12/4+0.25" };char lastExpression[MAX_SIZE * 2] = "";midToLast(midExpression, lastExpression);printf("%s\n", lastExpression);return 0;
}/*输出:
10 33 32 - 10 * + 12 4 / + 0.25 +输出:
*/
相关文章:

数据结构C语言描述4(图文结合)--栈的实现,中序转后序表达式的实现
前言 这个专栏将会用纯C实现常用的数据结构和简单的算法;有C基础即可跟着学习,代码均可运行;准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言…...

python基本数据类型 -- 元组tuple
在 Python 中,元组(Tuple)是一种轻量级的、不可变的数据结构。与列表类似,元组用于存储有序的数据集合,但它一旦创建,其内容就无法更改。这种特性让元组在某些场景下更加安全和高效。本文将从定义、操作、应…...

tcpdump交叉编译
TCPDUMP在Libpcap上开发。 首先需要编译libcap。 网上那么多教程,下载地址都只给了一个英文的官网首页, 你尽可以试试,从里面找到下载地址都要费半天时间。 \color{red}网上那么多教程,下载地址都只给了一个英文的官网首页&#…...

Spring IOC注入方式、Bean作用域
Spring IOC注入 手动注入 set方法注入 需要提供set方法 public class UserService {private UserDao userDao; public void setUserDao(UserDao userDao) {this.userDao userDao;} } 设置属性字段的值 <bean id"userService" class"com.shsxt.servi…...

uniapp微信小程序转发跳转指定页面
onShareAppMessage 是微信小程序中的一个重要函数,用于自定义转发内容。当用户点击右上角的菜单按钮,并选择“转发”时,会触发这个函数。开发者可以在这个函数中返回一个对象,用于定义分享卡片的标题、图片、路径等信息。 使用场…...

利用uniapp开发鸿蒙:运行到鸿蒙模拟器—踩坑合集
从uniapp运行到鸿蒙模拟器上这一步,就有非常多的坑,一些常见的坑,官网都有介绍,就不再拿出来了,这里记录一下官网未记录的大坑 1.运行路径从hbuilderx启动鸿蒙模拟器 解决方法: Windows系统,官…...

【Vue】Vue3.0(二十五)Vue3.0中的具名插槽 的概念和使用场景
上篇文章 【Vue】Vue3.0(二十四)Vue3.0中 r e f s 、 refs 、 refs、parent 的概念和使用场景 🏡作者主页:点击! 🤖Vue专栏:点击! ⏰️创作时间:2024年11月20日16点30分 …...

【pytorch-02】:张量的索引、形状操作和常见运算函数
文章目录 1 张量索引1.1 简单行列索引和列表索引1.2 布尔索引和多维索引 2 张量的形状操作2.1 reshape函数2.2 transpose和permute函数的使用2.3 view和contiguous函数2.4 squeeze和unsqueeze函数用法2.5 张量更改形状小结 3 常见运算函数 1 张量索引 1.1 简单行列索引和列表索…...

C语言-指针作为函数返回值及二级指针
1、指针作为函数返回值 c语言允许函数的返回值是一个指针(地址)我们将这样的函数称为指针函数,下面的例子定义一了一个函数strlong(),用来返回两个字符串中较长的一个: 1. #include <stdio…...

css 使用图片作为元素边框
先看原始图片 再看效果 边框的四个角灭有拉伸变形,但是图片的中部是拉伸的 代码 border-style: solid;/* 设置边框图像的来源 */border-image-source: url(/static/images/mmwz/index/bk_hd3x.png);/* 设置如何切割图像 */border-image-slice: 66;/* 设置边框的宽度 */border…...

Linux无sudo权限将zsh作为默认shell
由于我只有用户权限,没有sudo权限,将zsh作为用户默认shell需要密码,所以没法在系统层面进行操作,下面另寻他法。 安装zsh 可以根据网上教程去安装zsh,一般电脑上会带有zsh,可以使用下述命令查看是否安装z…...

【React 进阶】掌握 React18 全部 Hooks
一、数据更新驱动 1. useState 1. 基础介绍 useState主要用于声明和操作状态变量,可以使函数组件像类组件一样拥有state const [state, setState] useState(initialState);state:状态,作为渲染视图的数据源 setState:改变st…...

【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter) 更新以gitee为准: 文章目录 数据预测概念和适用方式线性系统的适用性 数据预测算法和卡尔曼滤波公式推导状态空间方程和观测器先验估计后验估计…...

【SQL50】day 2
目录 1.每位经理的下属员工数量 2.员工的直属部门 3.判断三角形 4.上级经理已离职的公司员工 5.换座位 6.电影评分 7.修复表中的名字 8.患某种疾病的患者 9.删除重复的电子邮箱 1.每位经理的下属员工数量 # Write your MySQL query statement below #e1是经理,…...

【内存管理】理解 `WeakReference` 以更好地管理 Android 应用中的内存
在 Android 应用开发中,内存管理至关重要。糟糕的内存管理可能导致“内存泄漏”,即一些不再需要的对象仍然留在内存中,最终导致性能下降,甚至应用崩溃。WeakReference 就是帮助解决这个问题的一种工具。在本文中,我们将…...

解决IDEA中Maven管理界面不是层级结构的问题
文章目录 0. 前言1. 点击Maven管理界面右上角的三个点2. 勾选将模块分组3. 分组后的层级结构 更多 IDEA 的使用技巧可查看 IDEA 专栏中的文章:IDEA 0. 前言 在 IDEA 中,如果项目中有很多子模块,每个子模块中又有一个或多个子模块时…...

Linux运维篇-iscsi存储搭建
目录 概念实验介绍环境准备存储端软件安装使用targetcli来管理iSCSI共享存储 客户端软件安装连接存储 概念 iSCSI是一种在Internet协议上,特别是以太网上进行数据块传输的标准,它是一种基于IP Storage理论的存储技术,该技术是将存储行业广泛…...

深度学习基础练习:代码复现transformer重难点
2024/11/10-2024/11/18: 主要对transformer一些比较难理解的点做了一些整理,希望对读者有所帮助。 前置知识: 深度学习基础练习:从pytorch API出发复现LSTM与LSTMP-CSDN博客 【神经网络】学习笔记十四——Seq2Seq模型-CSDN博客 【官方双语】一…...

141. Sprite标签(Canvas作为贴图)
上节课案例创建标签的方式,是把一张图片作为Sprite精灵模型的颜色贴图,本节给大家演示把Canvas画布作为Sprite精灵模型的颜色贴图,实现一个标签。 注意:本节课主要是技术方案讲解,默认你有Canvas基础,如果没有Canvas基…...

【IDEA】解决总是自动导入全部类(.*)问题
文章目录 问题描述解决方法 我是一名立志把细节说清楚的博主,欢迎【关注】🎉 ~ 原创不易, 如果有帮助 ,记得【点赞】【收藏】 哦~ ❥(^_-)~ 如有错误、疑惑,欢迎【评论】指正探讨,我会尽可能第一时间回复…...

python中的OS模块的基本使用
🎉🎉🎉欢迎来到我的博客,我是一名自学了2年半前端的大一学生,熟悉的技术是JavaScript与Vue.目前正在往全栈方向前进, 如果我的博客给您带来了帮助欢迎您关注我,我将会持续不断的更新文章!!!🙏🙏🙏 文章目录…...

【Qt】QComboBox设置默认显示为空
需求 使用QComboBox,遇到一个小需求是,想要设置未点击出下拉列表时,内容显示为空。并且不想在下拉列表中添加一个空条目。 实现 使用setPlaceholderText()接口。我们先来看下帮助文档: 这里说的是,placeholderText是…...

LeetCode - #139 单词拆分
文章目录 前言摘要1. 描述2. 示例3. 答案题解动态规划的思路代码实现代码解析1. **将 wordDict 转换为 Set**2. **初始化 DP 数组**3. **状态转移方程**4. **返回结果** **测试用例**示例 1:示例 2:示例 3: 时间复杂度空间复杂度总结关于我们 前言 本题由于没有合适答案为以往遗…...

服务器作业4
[rootlocalhost ~]# vim 11.sh #关闭防火墙 systemctl stop firewalld setenforce 0 #1.接收用户部署的服务名称 read -p "服务名称:(nginx)" server_name if [ $server_name ! nginx ];then echo "输入的不是nginx,脚本退出" exit 1 fi # 判断是…...

IOC控制反转---相关的介绍和6大注解解读(类注解+方法注解)
文章目录 1.传统方式造车2.传统方法的弊端3.IOC的引入3.IOC对于图书管理系统进行改进(初识)4.注解的使用说明4.1controller注解4.2service注解4.3component注解4.4关于spring命名的问题4.5component重命名4.6repository注解4.7configuration注解4.8注解之…...

SpringBoot(8)-任务
目录 一、异步任务 二、定时任务 三、邮件任务 一、异步任务 使用场景:后端发送邮件需要时间,前端若响应不动会导致体验感不佳,一般会采用多线程的方式去处理这些任务,但每次都需要自己去手动编写多线程来实现 1、编写servic…...

【机器学习】如何配置anaconda环境(无脑版)
马上就要上机器学习的实验,这里想写一下我配置机器学习的anaconda环境的二三事 一、首先,下载安装包: Download Now | Anaconda 二、打开安装包,一直点NEXT进行安装 这里要记住你要下载安装的路径在哪,后续配置环境…...

java 可以跨平台的原因是什么?
我们对比一个东西就可以了,那就是chrome浏览器。 MacOS/Linux/Windows上的Chrome浏览器,那么对于HTML/CSS/JS的渲染效果都一样的。 我们就可以认为ChromeHTML/CSS/JS是跨平台的。 这里面,HTML/CSS/JS是不变的的,对于一个网页&a…...

Solana应用开发常见技术栈
编程语言 Rust Rust是Solana开发中非常重要的编程语言。它具有高性能、内存安全的特点。在Solana智能合约开发中,Rust可以用于编写高效的合约代码。例如,Rust的所有权系统可以帮助开发者避免常见的内存错误,如悬空指针和数据竞争。通过合理利…...

npm | Yarn | pnpm Node.js包管理器比较与安装
一、包管理器比较 参考原文链接: 2024 Node.js Package Manager 指南:npm、Yarn、pnpm 比较 — 2024 Node.js Package Manager Guide: npm, Yarn, pnpm Compared (nodesource.com) 以下是对 Node.js 的三个包管理工具 npm、Yarn 和 pnpm 的优缺点总结&am…...