数据结构实验1:栈和队列的应用

目录

一、实验目的

二、实验原理

1.1栈的基本操作

1.1.1 栈的定义

1.1.2 初始化栈

1.1.3 压栈(Push)

1.1.4 出栈(Pop)

1.1.5 判空(isEmpty)

1.1.6  查看栈顶元素(Top)

1.1.7 获取栈的大小(Size)

2.1栈的应用

三、实验内容

中缀表达式的计算 

原理

代码

截图

分析


一、实验目的

1、理解并掌握栈和队列的逻辑结构和存储结构;理解栈和队列的相关基本运算;

2、编程对相关算法进行验证;

3、学会利用栈和队列解决实际问题。

二、实验原理

栈(Stack)是一种基本的数据结构,它遵循先进后出(Last In, First Out,LIFO)的原则。这意味着最后进入栈的元素是第一个被移除的,而最先进入栈的元素是最后被移除的。栈常常被用于管理函数调用、表达式求值、内存管理等各种应用场景。

1.1栈的基本操作

1.1.1 栈的定义

#include<iostream>
#define MAX_SIZE 100//最大为100个元素,且为全局变量typedef struct{int data[MAX_SIZE];//这里定义的是int型数组,也可以定义其他类型的数组int top;//栈顶,指向末尾元素的起始地址
}Stack;

可以把栈当作一个数组看待,data数组最多储存100个元素,从0~99,top的范围为-1~98

若对结构体定义不清晰,可以参考 https://blog.csdn.net/weixin_58628068/article/details/135318742

1.1.2 初始化栈

void Initialize(Stack *stack) {//参数为指针stack->top = -1;//由于栈底指向末尾元素的起始地址,空栈没有元素,则指向-1
}

1.1.3 压栈(Push)

将元素添加到栈的顶部。

void Push(Stack* stack, int elem) {if (stack->top >= (MAX_SIZE - 1)) {//若超出栈的表示范围cout << "栈满,无法添加元素";}else {stack->data[++stack->top] = elem;}
}

top的范围为-1~98,且top是从-1开始的,数组是从0开始的,则应该先自增再赋值。

1.1.4 出栈(Pop)

从栈的顶部移除元素,并返回。

int Pop(Stack* stack) {if (stack->top == -1) {//如果栈为空cout << "栈为空,无元素";return -1;}else {int c=stack->data[stack->top];stack->top--;return c;}
}

数组从0开始,top是从-1开始,则应该+1,得到所需元素后应该指针-1

补充一下++x与x++的区别

一个是先对x加1再使用x的值

一个是先使用x的值再对x加1

--x与x--同理

1.1.5 判空(isEmpty)

 检查栈是否为空。

int IsEmpty(Stack* stack) {if (stack->top == -1) {//如果为空,则返回1return 1;}else {//否则返回0return 0;}
}

1.1.6  查看栈顶元素(Top)

返回栈顶元素,但不改变栈,与Pop操作有区别。

int Top(Stack* stack) {if (stack->top == -1) {//如果栈为空cout << "栈为空,无元素";return -1;}else {return stack->data[stack->top];}
}

注意Pop中是--,对top的值进行了改变,并未对top的值进行改变。

1.1.7 获取栈的大小(Size)

int Size(Stack* stack) {return stack->top+1;
}

数组从0开始,top是从-1开始,所以应该+1。

2.1栈的应用

栈的应用:

  • 函数调用: 函数调用时,局部变量和函数参数被推入栈,函数执行完毕后再从栈中弹出。
  • 表达式求值: 中缀表达式转换为后缀表达式,然后通过栈进行求值。
  • 内存管理: 操作系统使用栈来管理函数调用和局部变量。
  • Undo功能: 许多应用程序使用栈来实现撤销操作。

三、实验内容

中缀表达式的计算 

1、【问题描述】输入一个中缀表达式,将其转换为后缀表达式,然后对后缀表达 式进行求值。运算符包括“+”、“-”、“*”“/”、“(”“) ”、“#”,参加运算的数为小于 10 的自然数。

2、【输入要求】多组数据,每组数据一行,对应一个算术表达式,每个表达式均 以“#”结尾。当表达式只有一个“#”时,输入结束。 1

3、【输出要求】对于每组数据输出 2 行,第一行为中缀表达式对应的后缀式,第 2 行为后缀求值的运算结果。

原理
  1. 遍历中缀表达式: 从左到右扫描中缀表达式中的每个字符。

  2. 操作数处理: 遇到操作数时,直接输出到后缀表达式。

  3. 运算符处理:

    • 如果遇到左括号 '(',将其推入运算符栈。
    • 如果遇到右括号 ')',则将运算符栈中的运算符弹出并输出到后缀表达式,直到遇到左括号 '('。左括号 '(' 不输出到后缀表达式,而是从栈中弹出。
    • 如果遇到其他运算符,比较其优先级与运算符栈栈顶的运算符优先级:
      • 如果当前运算符的优先级小于或等于栈顶运算符的优先级,将栈顶运算符弹出并输出到后缀表达式,然后将当前运算符推入运算符栈。
      • 如果当前运算符的优先级大于栈顶运算符的优先级,将当前运算符推入运算符栈。
  4. 表达式结束处理: 遍历完中缀表达式后,将运算符栈中的所有运算符依次弹出并输出到后缀表达式。

  5. 计算后缀表达式: 使用一个栈来计算后缀表达式。

    • 遍历后缀表达式中的每个字符:
      • 如果是操作数,将其推入操作数栈。
      • 如果是运算符,从操作数栈中弹出足够的操作数,执行相应的运算,将结果推入操作数栈。
  6. 最终结果: 在计算完成后,操作数栈中的唯一元素即为最终结果。

代码
#include<iostream>
using namespace std;#define MAX_SIZE 100
char op_list[6] = { '+','-','*','/','(',')' };//操作符typedef struct Stack {char data[MAX_SIZE];int top;
};void Initialize(Stack* stack) {//参数为指针stack->top = -1;//由于栈底指向末尾元素的起始地址,空栈没有元素,则指向-1
}void Push(Stack* stack, char elem) {if (stack->top >= (MAX_SIZE - 1)) {//若超出栈的表示范围cout << "栈满,无法添加元素";}else {stack->data[++stack->top] = elem;}
}char Pop(Stack* stack) {if (stack->top == -1) {//如果栈为空cout << "栈为空,无元素";return  '#';}else {char c=stack->data[stack->top];stack->top--;return c;}
}
int IsEmpty(Stack* stack) {if (stack->top == -1) {//如果为空,则返回1return 1;}else {//否则返回0return 0;}
}char Top(Stack* stack) {if (stack->top == -1) {//如果栈为空cout << "栈为空,无元素"<<endl;return '#';}else {return stack->data[stack->top];}
}int Size(Stack stack) {return stack.top + 1;
}int main() {int count = 0;//字符串大小int index = 0;//后缀表达式的起始值char exp_infix[MAX_SIZE] = {'\0'};//字符串中缀表达式char exp_postfix[MAX_SIZE] = { '\0' };//字符串后缀表达式Stack stack_num, stack_op;//操作数栈和操作符栈//初始化栈Initialize(&stack_num);Initialize(&stack_op);for (int i = 0; i < MAX_SIZE; i++) {cin >> exp_infix[i];if (exp_infix[i]=='#') {//输入结束break;}count++;}//cout << count << "个数" << endl;for(int i=0;i<count;i++){if ((exp_infix[i] <= '9') && (exp_infix[i] >= '0'))//如果输入的是数字,直接复制{exp_postfix[index++] = exp_infix[i];//cout << exp_infix[i] << "压入num栈中" << endl;}else {//如果为操作符if (exp_infix[i] == '(') {//如果是 ( ,直接入操作符栈//cout << "遇到 (" << endl;Push(&stack_op, '(');}else if (exp_infix[i] == ')') {//如果是 ) ,则从操作符的栈顶到栈底,从栈顶到最近的一个(之间的所有操作符压出;//cout << "遇到 )" << endl;while (Top(&stack_op) != '(') {//cout << "栈顶为" << Top(&stack_op) << endl;char c = Pop(&stack_op);//cout << "出栈为" << c<<endl;exp_postfix[index++] = c;}Pop(&stack_op);//将 ( 出栈}else {//如果为 + - * /,将优先级大于等于它的压出,直到前面没有元素可以再压出,再入栈if ((exp_infix[i] == '*') || (exp_infix[i] == '/')) {//如果为*或者/,则需要压出*或者///cout << "遇到" << exp_infix[i] << endl;while ((IsEmpty(&stack_op) == 0) && (Top(&stack_op) != '+') && (Top(&stack_op) != '-') && (Top(&stack_op) != '(')) {//如果栈为空或者遇到了小于等于*和/的元素,则不应该继续压出//cout << "出栈" << Top(&stack_op) << endl;char c = Pop(&stack_op);exp_postfix[index++] = c;//cout << "出栈为" << c << endl;}Push(&stack_op, exp_infix[i]);//入栈}else {//如果为+或者-,则除了(都应该出栈//cout << "遇到" << exp_infix[i] << endl;while ((IsEmpty(&stack_op) == 0) && (Top(&stack_op) != '(')) {//如果为空或者遇到了(,则停止出栈//cout << "出栈" << Top(&stack_op) << endl;char c = Pop(&stack_op);//cout << "出栈为" << c << endl;exp_postfix[index++] = c;}Push(&stack_op, exp_infix[i]);//入栈}}}}//判断op栈是否为空,若不为空,则全部出栈while (IsEmpty(&stack_op) == 0) {//cout << "出栈" << Top(&stack_op) << endl;char c = Pop(&stack_op);//cout << "出栈为" << c << endl;exp_postfix[index++] = c;}//cout << "index:" << index<< endl;for (int i = 0; i < index; i++) {cout << exp_postfix[i];}cout << endl;//计算//初始化栈Initialize(&stack_num);Initialize(&stack_op);int num = 0;for (int i = 0; i < index; i++) {if ((exp_postfix[i] <= '9') && (exp_postfix[i] >= '0'))//如果输入的是数字,直接压入操作数栈{Push(&stack_num, exp_postfix[i]);}else {//如果是操作符,则依次从操作数栈中依次取出两个数,a2 op a1=a3,a3压入栈中int a1 = int(Pop(&stack_num))-48;int a2 = int(Pop(&stack_num)) - 48;int a3;switch (exp_postfix[i]) {case'*':a3 = a2 * a1; break;case'/':a3 = a2 / a1; break;case'+':a3 = a2 + a1; break;case'-':a3 = a2 - a1; break;}if (IsEmpty(&stack_num) == 1) {//如果为空栈cout << a3;}Push(&stack_num, char(a3 + 48));}}return 0;
}
截图

分析
  1. 数据结构定义:

    定义了一个结构体 Stack 用于表示栈,包含一个字符数组 data 作为栈的存储空间,以及一个整数 top 表示栈顶的索引。

  2. 栈的基本操作:

    • Initialize: 初始化栈。
    • Push: 将元素压入栈中。
    • Pop: 弹出栈顶元素。
    • IsEmpty: 判断栈是否为空。
    • Top: 获取栈顶元素。
    • Size: 获取栈的大小。
  3. 中缀表达式转后缀表达式:

    • 遍历输入的中缀表达式字符串 exp_infix
    • 使用两个栈 stack_numstack_op 分别存储操作数和操作符。
    • 遇到数字直接加入后缀表达式 exp_postfix 中。
    • 遇到左括号 '(' 直接入栈。
    • 遇到右括号 ')' 弹出操作符栈中的元素,直到遇到左括号 '(',并将这对括号之间的所有操作符加入后缀表达式。
    • 遇到运算符,根据优先级判断是否弹出栈中的运算符,直到满足条件为止。
    • 最终将剩余的操作符出栈。
  4. 后缀表达式计算:

    • 遍历后缀表达式字符串 exp_postfix
    • 遇到数字则入操作数栈。
    • 遇到操作符则从操作数栈中弹出两个数进行相应的运算,然后将结果入栈。
    • 最终操作数栈中的唯一元素即为计算结果。
  5. 注意点:

    • 对于数字字符的处理,将字符转换为对应的数字,例如 int(a) - 48
    • 将计算结果重新转换为字符入栈,例如 char(a3 + 48)
  6. 输出:

    • 输出转换后的后缀表达式。
    • 输出计算结果。

对于注释掉的代码,是为了简洁,如果不懂,请别注释掉,这个可以帮住同学们了解更详细的流程。 

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

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

相关文章

el-date-picker日期时间选择器限制可选的日期范围

业务场景&#xff1a;需要限制日期时间选择器可选择的日期&#xff0c;有两种模式&#xff0c; 一种是已知范围&#xff0c;只能选已知范围内的日期&#xff0c; 另一种是知道最近天数&#xff0c;只能选今天往前的天数内的日期&#xff0c;超出不能选。 <el-date-picker v-…

软性演员-评论家算法 SAC

软性演员-评论家算法 SAC 软性演员-评论家算法 SAC优势原理软性选择模型结构目标函数重参数化熵正则化算法步骤代码实现 软性演员-评论家算法 SAC 优势原理 DDPG 的问题在于&#xff0c;训练不稳定、收敛差、依赖超参数、不适应复杂环境。 软性演员-评论家算法 SAC&#xff…

算法通关村番外篇-优先队列

大家好我是苏麟 , 今天聊聊优先队列 . 优先队列 我们了解优先队列之前先说说队列 . 队列的特点是什么&#xff1f; 队列的特点是先进先出&#xff08;FIFO&#xff09;。 入队列&#xff0c;将新元素置于队尾&#xff1a; 出队列&#xff0c;队头元素最先被移出&#xff1a…

从新手到大师:四大编程范式解锁你的编码力!

编程&#xff0c;就是用代码跟计算机交流&#xff0c;告诉它我们想要它做什么。不同的编程范式就是不同的交流方式&#xff0c;每种方式都有自己独特的语法和规则。 今天&#xff0c;我们就来聊聊这四种主要的编程范式&#xff0c;它们分别是命令式、函数式、面向对象和声明式…

Java 堆的设计,如何用堆进行排序

Java 学习面试指南&#xff1a;https://javaxiaobear.cn 1、堆的定义 堆是计算机科学中一类特殊的数据结构的统称&#xff0c;堆通常可以被看做是一棵完全二叉树的数组对象。 1、堆的特性 它是完全二叉树&#xff0c;除了树的最后一层结点不需要是满的&#xff0c;其它的每一层…

听GPT 讲Rust源代码--compiler(33)

File: rust/compiler/rustc_middle/src/macros.rs 在Rust源代码中&#xff0c;rust/compiler/rustc_middle/src/macros.rs文件的作用是定义了一些用于宏展开的辅助宏和宏规则。 首先&#xff0c;这个文件导入了许多其他相关的模块&#xff0c;这些模块定义了编译器内部所需要的…

详解Java中的原子操作

第1章&#xff1a;什么是原子操作 大家好&#xff0c;我是小黑&#xff0c;面试中一个经常被提起的话题就是“原子操作”。那么&#xff0c;到底什么是原子操作呢&#xff1f;在编程里&#xff0c;当咱们谈论“原子操作”时&#xff0c;其实是指那些在执行过程中不会被线程调度…

1329:【例8.2】细胞 广度优先搜索

1329&#xff1a;【例8.2】细胞 时间限制: 1000 ms 内存限制: 65536 KB 【题目描述】 一矩形阵列由数字0 到9组成,数字1到9 代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。如: 4 10 0234500067 1034560500 2045600671 00000000…

最新-mybatis-plus 3.5分页插件配置

mybatis-plus 3.5分页插件配置 前提 1.项目不是springboot, 是以前的常规spring项目 2.mp 从3.2升级到3.5&#xff0c;升级后发现原本的分页竟然不起作用了&#xff0c;每次查询都是查出所有 前后配置对比 jar包对比 jsqlparser我这里单独引了包&#xff0c;因为版本太低…

数据结构入门到入土——链表(2)

目录 一&#xff0c;与链表相关的题目&#xff08;2&#xff09; 1.输入两个链表&#xff0c;找出它们的第一个公共节点 2.给定一个链表&#xff0c;判断链表中是否有环 3.给定一个链表&#xff0c;返回链表开始入环的第一个节点&#xff0c;若无则返回null 一&#xff0c;…

java字节码

1. 字节码 1.1 什么是字节码&#xff1f; Java之所以可以“一次编译&#xff0c;到处运行”&#xff0c;一是因为JVM针对各种操作系统、平台都进行了定制&#xff0c;二是因为无论在什么平台&#xff0c;都可以编译生成固定格式的字节码&#xff08;.class文件&#xff09;供…

机器学习模型可解释性的结果分析

模型的可解释性是机器学习领域的一个重要分支&#xff0c;随着 AI 应用范围的不断扩大&#xff0c;人们越来越不满足于模型的黑盒特性&#xff0c;与此同时&#xff0c;金融、自动驾驶等领域的法律法规也对模型的可解释性提出了更高的要求&#xff0c;在可解释 AI 一文中我们已…

案例介绍|钡铼助力2023年全国职业院校技能大赛工业网络智能控制与维护赛项

如今&#xff0c;越来越多的企业开始意识到数字制造和工业物联网已经成为工业自动化中大规模生产的核心驱动力。这其中&#xff0c;工业网络作为基础设施&#xff0c;是实现工厂设备联网与数据采集&#xff0c;建设数字工厂的基础和前提&#xff0c;甚至成为关乎数字工厂能否真…

给您的应用添加弹窗

概述 在我们日常使用应用的时候&#xff0c;可能会进行一些敏感的操作&#xff0c;比如删除联系人&#xff0c;这时候我们给应用添加弹窗来提示用户是否需要执行该操作&#xff0c;如下图所示&#xff1a; 弹窗是一种模态窗口&#xff0c;通常用来展示用户当前需要的或用户必须…

thinkphp学习02-目录结构、控制器、路由、配置文件

目录结构 www WEB部署目录&#xff08;或者子目录&#xff09; ├─app 应用目录 │ ├─controller 控制器目录 │ ├─model 模型目录 │ ├─ ... 更多类库目录 │ │ │ ├─common.php 公共函数文件 │ └─event.ph…

鸿蒙OS:不止手机,是物联网应用开发

鸿蒙开发是华为自主研发的面向全场景的分布式操作系统&#xff0c;旨在将生活场景中各类终端进行整合&#xff0c;实现不同终端设备间的快速连接、资源共享、匹配合适设备、提供流畅的全场景体验。 鸿蒙开发具有以下特点&#xff1a; 面向全场景&#xff1a;鸿蒙系统能够覆盖…

【51单片机】独立按键控制LED灯

不同于上篇文章只用代码控制&#xff0c;这次我们要再加上独立按键一同控制LED灯 目录 独立按键控制LED亮灭&#xff1a;代码实现&#xff1a; 独立按键控制LED状态&#xff1a;代码实现&#xff1a; 独立按键实现二进制LED显示&#xff1a;代码实现&#xff1a; 独立按键控制…

超声波模块的驱动(STM32、51单片机等)

一、前言 本文旨在分享单片机对超声波模块的驱动&#xff0c;测量距离和显示 二、超声波的驱动 1、超声波模块 2、模块性能 &#xff08;1&#xff09;TCT40-16T/R1 压电陶瓷超声传感器&#xff08;通用型&#xff09; 3、接口定义 Vcc、 Trig&#xff08;控制端&#xff09…

FreeRTOS移植详解

一、前言 本文旨在讲解FreeRTOS在STM32单片机上的移植步骤&#xff0c;对于FreeRTOS在其他单片机上的移植已具有一定的参考意义。相信读者在看完这篇文章后&#xff0c;一定会有所收获&#xff01; 文末附有相关资料连接&#xff0c;有需要的读者可以自行下载。 二、FreeRTOS源…

OpenSource - 基于Netty的网络扩展库HServer

文章目录 概述官网Hserver的理念特点原理图代码案例HelloWorld 概述 HServer是一个基于Netty开发网络扩展库.使用插件方式来扩展我们的业务 HServer提供 web,gateway,rpc 等插件 同时用户也可以自定义插件&#xff0c;来完成各种各样的业务场景。 官网 https://gitee.com/HSe…