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

news2024/12/25 1:55:08

目录

一、实验目的

二、实验原理

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) {//如果为空,则返回1
		return 1;
	}
	else {//否则返回0
		return 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) {//如果为空,则返回1
		return 1;
	}
	else {//否则返回0
		return 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.coloradmin.cn/o/1364470.html

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

相关文章

【好书推荐】我的第一本科技漫画书:漫画区块链

王杰&#xff0c;南京理工大学物理电子学硕士&#xff0c;曾担任乐视VR技术总监&#xff0c;现为北京米唐科技有限公司CEO&#xff0c;知乎“区块链”领域知名作者&#xff0c;北京信息科技大学、北京建筑大学、北京信息职业技术学院客座教授。 郑巍&#xff0c;擅长绘制钢笔淡…

application.properties 如何改成 application.yml

Convert YAML and Properties File 右键直接转换即可 Further Reading &#xff1a; idea 常用插件

14_IO_其他流

文章目录 数据流DataOutputStream数据输出流DataInputStream数据输入流 打印流PrintStream字节打印流PrintWriter字符打印流 标准输入输出流标准输入流标准输出流 对象流(序列化与反序列化流)ObjectOutputStream序列化流ObjectInputStream反序列化流 RandomAccessFile随机访问文…

【Harmony OS - 网络请求】

在一个应用开发中&#xff0c;网络请求是必不可少的&#xff0c;我们一般用的fetch、axios来进行http请求&#xff0c;在鸿蒙中也可以通过createHppt来发生一个http请求&#xff0c;它们都是异步请求返回的Promise&#xff0c;下面我们将介绍’ohos.net.http’和axios这两种方式…

(21)Linux的文件描述符输出重定向

一、文件描述符 1、文件描述符的底层理解 在上一章中&#xff0c;我们已经把 fd 的基本原理搞清楚了&#xff0c;知道了 fd 的值为什么是 0,1,2,3,4,5... 也知道了 fd 为什么默认从 3 开始&#xff0c;而不是从 0,1,2&#xff0c;因为其在内核中属于进程和文件的对应关系。 …

Mysql SQL审核平台Yearning本地部署

文章目录 前言1. Linux 部署Yearning2. 本地访问Yearning3. Linux 安装cpolar4. 配置Yearning公网访问地址5. 公网远程访问Yearning管理界面6. 固定Yearning公网地址 前言 Yearning 简单, 高效的MYSQL 审计平台 一款MYSQL SQL语句/查询审计工具&#xff0c;为DBA与开发人员使用…

Python的基础练习题之学生管理系统

需求 使用Python基础写一个基于控制台的学生管理平台&#xff0c;里面功能分别是&#xff1a;1.录入学生信息2.查找学生信息3.删除学生信息4.修改学生信息5.排序6.统计学生总人数7.显示所有学生信息&#xff0c;要求数据存储在文件里。 代码 代码资源地址可以直接下载 效果图…

05 Ciso模拟器连接腾讯云物联网开发平台

Ciso声明&#xff1a;本篇文章基于使用腾讯云物联网平台连接自定义esp8266物联网设备(腾讯连连控制开关实现) - CSDN App改编 一、总体概览 功能描述&#xff1a; 使用腾讯连连小程序进行控制&#xff0c; Alarm&#xff08;警铃&#xff09;&#xff1a;开的时候&#xff…

【AI视野·今日Robot 机器人论文速览 第六十九期】Wed, 3 Jan 2024

AI视野今日CS.Robotics 机器人学论文速览 Wed, 3 Jan 2024 Totally 5 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers NID-SLAM: Neural Implicit Representation-based RGB-D SLAM in dynamic environments Authors Ziheng Xu, Jianwei Niu, Qingf…

实现在一个文件夹中找到特定名称特点格式的文件

当你要在一个文件夹中查找特定名称和格式的文件时&#xff0c;你可以使用 Python 的 os 和 fnmatch 模块。以下是一个简单的脚本示例&#xff0c;它可以在指定目录中查找文件&#xff1a; import os import fnmatchdef find_files(directory, pattern):"""在指…

C#使用栈方法遍历二叉树

步骤一&#xff1a;定义一个二叉树的节点类 定义一个二叉树的节点类&#xff0c;其中包含节点的值、左子节点和右子节点。 // 二叉树节点定义public class TreeNode{public int Value { get; set; } // 节点的值public TreeNode Left { get; set; } // 左子节点public TreeN…

Java Base64简单介绍

1. Base64工具 工具链接 2. Base64示例代码 public class Base64Demo {// 请注意&#xff0c;在处理二进制数据时&#xff08;例如图片或文件&#xff09;&#xff0c;不需要将字节数组转换为字符串再进行编码或解码&#xff0c;// 可以直接对字节数组进行Base64操作。上述…

Python基础知识总结3-面向对象进阶知识

面向对象三大特征介绍 继承子类扩展父类语法格式关于构造函数&#xff1a;类成员的继承和重写查看类的继承层次结构 object根类dir() 查看对象属性重写 __str__() 方法 多重继承MRO方法解析顺序super()获得父类定义多态特殊方法和运算符重载特殊属性 对象的浅拷贝和深拷贝组合_…

学习笔记——C++一维数组

1&#xff0c;一维数组的定义方式 三种定义方式 1&#xff0c;数据类型 数组名[ 数组长度 ]&#xff1b; 2&#xff0c;数据类型 数组名[ 数组长度 ]{值1&#xff0c;值2&#xff0c;值3 ……}&#xff1b;//未说明的元素用0填补 3&#xff0c;数据类型 数组名[ ]{值1&…

【数据仓库与联机分析处理】数据仓库工具Hive

目录 一、Hive简介 &#xff08;一&#xff09;什么是Hive &#xff08;二&#xff09;优缺点 &#xff08;三&#xff09;Hive架构原理 &#xff08;四&#xff09;Hive 和数据库比较 二、MySQL的安装配置 三、Hive的安装配置 1、下载安装包 2、解压并改名 3、配置环…

嵌入式培训机构四个月实训课程笔记(完整版)-Linux系统编程第三天-Linux进程练习题(物联技术666)

更多配套资料CSDN地址:点赞+关注,功德无量。更多配套资料,欢迎私信。 物联技术666_嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记-CSDN博客物联技术666擅长嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记,等方面的知识,物联技术666关注机器学习,arm开发,物联网,嵌入式硬件,单片机…

React 中条件渲染的 N 种方法

本文作者系360奇舞团前端开发工程师 条件渲染在React开发中非常重要的功能&#xff0c;它允许开发人员根据条件控制渲染的内容&#xff0c;在创建动态和交互式用户界面方面发挥着至关重要的作用&#xff0c;本文总结了常用的的条件渲染方法。 1.If-else if-else是一种控制流程的…

rime中州韵小狼毫 敏感词脱敏滤镜

快速录入&#xff0c;是任何一个输入法&#xff0c;以及输入人员&#xff08;无论是否专业&#xff09;的追求目标之一。现实中&#xff0c;由于各种输入法在录入文本时&#xff0c;都无法完全避免重码的问题&#xff0c;所以在输入过程中都或多或少的需要进行选字/选词操作。这…

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

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

认识Git

&#x1f30e;初识Git 初识Git 什么是Git Git的安装       Centos平台安装Git       Ubuntu平台安装Git Git的基本操作       创建远程仓库       配置Git 认识工作区、暂存区与版本库       添加文件到暂存区       将暂存区文件提交至本…