栈应用后缀表达式计算

后缀表达式计算
LinkStack.cpp
#include<iostream>
using namespace std;
//单链表的结点结构体:
template <class DataType>
struct Node
{ DataType data;
Node<DataType> *next;
};
//带头结点的单链表类的声明
template <class DataType>
class LinkStack
{ public:
LinkStack( ){ top=NULL; } //构造函数
~LinkStack( ); //析构函数
bool IsEmpty( ); //栈空操作
DataType GetTop ( ); //得到栈顶值操作
void Push( DataType x ); //进栈操作
DataType Pop( ); //出栈操作
private:
Node<DataType> *top;
};
//析构函数,析构函数将单链表中所有结点的存储空间释放。

template <class DataType>
LinkStack<DataType> :: ~LinkStack( )
{ Node<DataType> *q;
while (top !=NULL)
{ q = top; //暂存释放结点
top = top->next; //top指向被释放结点的下一个结点 delete q; //释放结点
}
cout<<"链表已经删除。

"<<endl;
}
//栈空返回true,否则返回false
template <class DataType>
bool LinkStack<DataType>::IsEmpty()
{ return (top==NULL)?true:false; };
//进栈操作
template <class DataType>
void LinkStack<DataType>:: Push( DataType x )
{ Node<DataType> *s;
s=new Node<DataType>; //申请一个数据成为x的结点s s->data=x;
s->next=top;
top=s;
};
//出栈操作
template <class DataType>
DataType LinkStack<DataType>::Pop( )
{ Node<DataType> *p;
DataType x;
if (IsEmpty())
{ cout<< "栈空无值弹出" <<endl; exit(1); } x=top->data;
p=top;
top=top->next;
delete p;
return x;
};
//返回栈顶值操作
template <class DataType>
DataType LinkStack<DataType>::GetTop( )
{ if (IsEmpty())
{ cout<< "栈空无值" <<endl;
exit(1);
}
return top->data;
};
PostExp.cpp
#include "LinkStack.h"
//后缀表达式计算的栈类声明
template <class DataType>
class PostExp
{ public:
PostExp( ){ } ; //构造函数
void Run( ); //执行表达式计算
void Clear( ); //清栈操作
private:
LinkStack<DataType> s; //栈对象s
void Enter( DataType num); //进栈操作
bool In(char op); //判断操作数
bool GetTwoOperands(DataType& operand1,DataType& operand2); //从栈中推出两个操作数
void Compute(char op); //形成运算指令,进行计算
};
//进栈操作
template <class DataType>
void PostExp<DataType>::Enter( DataType num)
{ s.Push(num); } //将操作数的值num进操作数栈
//从栈中推出两个操作数
template <class DataType>
bool PostExp<DataType>::GetTwoOperands(DataType&
operand1,DataType& operand2)
{ if(s.IsEmpty()) //判断栈是否空
{ cout<< "缺少右操作数" <<endl; return false; }
operand1=s.Pop();
if(s.IsEmpty())
{ cout<< "缺少左操作数" <<endl; return false; }
operand2=s.Pop(); //去左操作数
return true;
}
//形成运算指令,进行计算
template <class DataType>
void PostExp<DataType>::Compute(char op)
{
bool retult;
DataType operand1,operand2;
retult=GetTwoOperands(operand1,operand2); //取两个操作数if(retult==true) //如果操作数取成功,计算并进栈
switch( op )
{
case '+': s.Push(operand2 + operand1); break;
case '-': s.Push(operand2 - operand1); break;
case '*': s.Push(operand2 * operand1); break;
case '/': if(operand1==0)
{ cout<< "除数为零" <<endl;
exit(1);
}
else
s.Push(operand2 / operand1);
break;
}
else s.~LinkStack();
}
//判断操作数
template <class DataType>
bool PostExp<DataType>::In(char op) //判断操作数
{
switch( op ) //是操作符进行计算
{
case '+' :
case '-' :
case '*' :
case '/' : return true;
default : return false;
}
}
//执行表达式计算
template <class DataType>
void PostExp<DataType>::Run( )
{
char ch;
DataType newOperand;
cout<< "输出后缀表达式,以#结束;" <<endl;
while( cin>>ch,ch!='#')
{
if(In(ch)) Compute(ch);
else //不是操作符,不进行计算
{ cin.putback(ch); //把字符放回操作流
cin>>newOperand; //从新读操作数
Enter(newOperand); //将操作数放入栈中
}
}
if(! s.IsEmpty())cout<< "后缀表达式是:" <<s.GetTop()<<endl; }
//清栈操作
template <class DataType>
void PostExp<DataType>:: Clear( )
{ s.~LinkStack(); }
test.cpp
#include "PostExp.h"
void main(void)
{
PostExp<int> exp;
exp.Run();
}。

合集下载

实验二--栈的应用---算术表达式的计算

实验二--栈的应用---算术表达式的计算

浙江大学城市学院实验报告课程名称数据结构与算法实验项目名称实验二栈的应用---算术表达式的计算实验成绩指导老师(签名)日期一.实验目的和要求1.进一步掌握栈的基本操作的实现。

2.掌握栈在算术表达式的计算方面的应用。

二. 实验内容1. 编写程序利用栈将中缀表达式转换成后缀表达式,即从键盘输入任一个中缀表达式(字符串形式),转换成后缀表达式后,将后缀表达式输出。

假设:中缀表达式包含圆括号( ) 及双目运算符+、-、*、/、^(乘方)。

要求:把栈的基本操作的实现函数存放在头文件stack1.h中(栈元素的类型为char),在主文件test6_2.cpp中包含将中缀表达式S1转换成后缀表达式S2的转换函数void Change( char *S1, char *S2 )及主函数,在主函数中进行输入输出及转换函数的调用。

2. 选做:编写利用栈对后缀表达式进行求值的函数double Compute(char *str),以计算从前述程序得到的后缀表达式的值。

要求:把栈的基本操作的实现函数存放在头文件stack2.h中(栈元素的类型为double),在主文件test6_2.cpp中添加后缀表达式求值函数,并在主函数中增加调用求值函数及输出结果值的语句。

3. 填写实验报告,实验报告文件取名为report2.doc。

4. 上传实验报告文件report2.doc与源程序文件stack1.h、stack2.h(若有)及test6_2.cpp到Ftp服务器上你自己的文件夹下。

二.函数的功能说明及算法思路(算法思路见源程序的注释部分)//栈的顺序存储结构定义struct Stack{ElemType *stack;int top;int MaxSize;};//初始化栈S为空void InitStack(Stack &S)//元素item进栈,即插入到栈顶void Push(Stack &S,ElemType item)//删除栈顶元素并返回ElemType Pop(Stack &S)//读取栈顶元素的值ElemType Peek(Stack &S)//判断S是否为空,若是则返回true,否则返回false bool EmptyStack(Stack &S)//清除栈S中的所有元素,释放动态存储空间void ClearStack(Stack &S)//将中缀算术表达式转换为后缀算术表达式void Change(char *S1,char *&S2)//返回运算符op所对应的优先级数值int Precedence(char op)//计算由str所指字符串的后缀表达式的值double Compute(char *str)四. 实验结果与分析五. 心得体会【附录----源程序】test6_2.cpp#include<iostream.h>#include<stdlib.h>#include<math.h>#include"stack1.h"#include"stack2.h"void main(){char x[30],y[30];double r;while(1){cout<<"请输入一个中缀算术表达式:";cin.getline(x,sizeof(x));Change(x,y);cout<<"对应的后缀算术表达式为:";cout<<y<<endl;r=Compute(y);cout<<"后缀算术表达式值为:"<<r<<endl<<endl;}}stack1.htypedef char ElemType1;struct Stack1{ElemType1 *stack;int top;int MaxSize;};void InitStack(Stack1 &S){S.MaxSize=10;S.stack=new ElemType1[S.MaxSize];if(!S.stack){cerr<<"动态储存分配失败"<<endl;exit(1);}S.top=-1;}void Push(Stack1 &S,ElemType1 item)if(S.top==S.MaxSize-1){int k=sizeof(ElemType1);S.stack=(ElemType1*)realloc(S.stack,2*S.MaxSize*k);S.MaxSize=2*S.MaxSize;}S.top++;S.stack[S.top]=item;}ElemType1 Pop(Stack1 &S){if(S.top==-1){cerr<<"Stack is empty! "<<endl;exit(1);}S.top--;return S.stack[S.top+1];}ElemType1 Peek(Stack1 &S){if(S.top==-1){cerr<<"Stack is empty! "<<endl;exit(1);}return S.stack[S.top];}bool EmptyStack(Stack1 &S){return S.top==-1;}void ClearStack(Stack1 &S){if(S.stack){delete []S.stack;S.stack=0;}S.top=-1;S.MaxSize=0;}//返回运算符op所对应的优先级数值int Precedence(char op){switch(op){case'+':case'-':return 1;case'*':case'/':return 2;case'^':return 3;case'(':case'@':default:return 0;}}//将中缀算术表达式转换为后缀算术表达式void Change(char *S1,char *S2){Stack1 R;InitStack(R);Push(R,'@');int i=0,j=0;char ch=S1[i];while(ch!='\0'){//对于空格字符不做任何处理,顺序读取下一个字符if(ch==' ')ch=S1[++i];//对于左括号,直接进栈else if(ch=='('){Push(R,ch);ch=S1[++i];}//对于右括号,使括号内的仍停留在栈中的运算符依次出栈并写入S2else if(ch==')'){while(Peek(R)!='(')S2[j++]=Pop(R);Pop(R);//删除栈顶的左括号ch=S1[++i];}//对于运算符,使暂存于栈顶且不低于ch优先级的运算符依次出栈并写入S2else if(ch=='+'||ch=='-'||ch=='*'||ch=='/'||ch=='^'){char w=Peek(R);while(Precedence(w)>=Precedence(ch)){S2[j++]=w;Pop(R);w=Peek(R);}Push(R,ch);//把ch运算符写入栈中ch=S1[++i];}else{if((ch<'0'||ch>'9')&&ch!='.'){cout<<"中缀表达式表示错误!"<<endl;exit(1);}while((ch>='0'&&ch<='9')||ch=='.'){S2[j++]=ch;ch=S1[++i];}S2[j++]=' ';}}//把暂存在栈中的运算符依次退栈并写入到S2中ch=Pop(R);while(ch!='@'){if(ch=='('){cerr<<"expression error!"<<endl;exit(1);}else{S2[j++]=ch;ch=Pop(R);}}S2[j++]='\0';}stack2.htypedef double ElemType2;struct Stack2{ElemType2 *stack;int top;int MaxSize;};void InitStack(Stack2 &S){S.MaxSize=10;S.stack=new ElemType2[S.MaxSize];if(!S.stack){cerr<<"动态储存分配失败"<<endl;exit(1);}S.top=-1;}void Push(Stack2 &S,ElemType2 item){if(S.top==S.MaxSize-1){int k=sizeof(ElemType2);S.stack=(ElemType2*)realloc(S.stack,2*S.MaxSize*k);S.MaxSize=2*S.MaxSize;}S.top++;S.stack[S.top]=item;}ElemType2 Pop(Stack2 &S){if(S.top==-1){cerr<<"Stack is empty! "<<endl;exit(1);}S.top--;return S.stack[S.top+1];}ElemType2 Peek(Stack2 &S){if(S.top==-1){cerr<<"Stack is empty! "<<endl;exit(1);}return S.stack[S.top];}bool EmptyStack(Stack2 &S){return S.top==-1;}void ClearStack(Stack2 &S){if(S.stack){delete []S.stack;S.stack=0;}S.top=-1;S.MaxSize=0;}//计算由str所指字符串的后缀表达式的值double Compute(char *str){Stack2 S;InitStack(S);double x,y;int i=0;while(str[i]){if(str[i]==' '){i++;continue;}switch(str[i]){case '+':x=Pop(S)+Pop(S);i++;break;case'-':x=Pop(S);x=Pop(S)-x;i++;break;case'*':x=Pop(S)*Pop(S);i++;break;case'/':x=Pop(S);if(x!=0)x=Pop(S)/x;else{cerr<<"Divide by 0!"<<endl;exit(1);}i++;break;case'^':x=Pop(S);x=pow(Pop(S),x);i++;break;default:x=0;while(str[i]>=48&&str[i]<=57){x=x*10+str[i]-48;i++;}if(str[i]=='.'){i++;y=0;double j=10.0;while(str[i]>=48&&str[i]<=57){y=y+(str[i]-48)/j;i++;j*=10;}x+=y;}}Push(S,x);}if(EmptyStack(S)){cerr<<"expression error!"<<endl;exit(1);}x=Pop(S);if(EmptyStack(S))return x;else{cerr<<"expression error!"<<endl;exit(1);}ClearStack(S);}(注:可编辑下载,若有不当之处,请指正,谢谢!)。

基于栈的后缀算术表达式求值c语言

基于栈的后缀算术表达式求值c语言

基于栈的后缀算术表达式求值c语言1. 引言1.1 概述本文将讨论基于栈的后缀算术表达式求值的实现过程。

后缀算术表达式(也称为逆波兰表达式)是一种无需括号即可进行运算的表达式表示方法,它将操作符置于操作数之后。

相较于传统的中缀表达式,在计算机程序中处理后缀表达式更为高效和简洁。

1.2 文章结构文章分为五个主要部分:引言、栈的概念及原理、后缀算术表达式的定义和转换、基于栈的后缀算术表达式求值算法实现以及结论与总结。

在引言部分,我们将首先介绍本文的概述和目标,对后续内容进行简要说明。

1.3 目的通过本文,我们旨在让读者了解栈数据结构的基本概念和原理,并且掌握如何利用栈来实现对后缀算术表达式进行求值的算法。

同时,我们将介绍后缀算术表达式的定义和转换方法,并给出基于栈实现该计算方式的详细步骤与示例代码。

通过深入研究并学习这些内容,读者可以加深对栈数据结构和后缀算术表达式的理解,并且能够应用所学知识解决实际问题。

本文不仅适用于计算机科学或相关专业的学生,也适合对数据结构和算法感兴趣的读者阅读和学习。

2. 栈的概念及原理2.1 栈的定义栈是一种具有特定限制条件的线性数据结构,它具备“先进后出”(Last-In-First-Out,LIFO)的特性。

栈可以看作是一个容器,其中可以存储各种类型的数据。

与实际生活中的堆栈类似,栈只允许在其末尾进行插入和删除操作。

在栈中,最后加入的元素首先被访问和处理。

这是由于栈内元素之间的相对位置关系决定的。

插入操作称为“压栈”(Push),删除操作称为“弹栈”(Pop),而从栈顶读取元素或获取栈顶元素但不删除它称为“查看”(Peek)。

2.2 栈的基本操作推入元素:将一个元素添加到栈顶。

如果已经存在满员条件,则无法执行此操作。

弹出元素:从栈顶移除一个元素,并返回移除的值。

如果没有任何元素存在,则无法执行此操作。

查看栈顶元素:获取位于栈顶处的元素值,但不对其进行删除。

判断是否为空:检查栈是否为空。

中最表达式-后缀表达式

中最表达式-后缀表达式
N=N/B;
}
while(!StackEmpty(&S)){?//栈非空时退栈输出
i=Pop(&S);
printf("%d",i);
}
}
例,求后缀表达式:1 2 + 8 2 - 7 4 - / *的值,
栈的变化情如下:
步骤
栈中元素
说明
1
1
1进栈
2
12
2进栈
3
遇+号退栈2和1
4
3
1+2=3的结果3进栈
5
38
8进栈
6
382
2进栈
7
3
遇-号退栈2和8
17
6
扫描完毕,运算结束
从上可知,最后求得的后缀表达式之值为6,与用中缀表达式求得的结果一致,但后缀式求值要简单得多。
五、中缀表达式变成等价的后缀表达式的算法
将中缀表达式变成等价的后缀表达式,表达式中操作数次序不变,运算符次序发生变化,同时去掉了圆括号。转换规则是:设立一个栈,存放运算符,首先栈为空,编译程序从左到右扫描中缀表达式,若遇到操作数,直接输出,并输出一个空格作为两个操作数的分隔符;若遇到运算符,则必须与栈顶比较,运算符级别比栈顶级别高则进栈,否则退出栈顶元素并输出,然后输出一个空格作分隔符;若遇到左括号,进栈;若遇到右括号,则一直退栈输出,直到退到左括号止。当栈变成空时,输出的结果即为后缀表达式。将中缀表达式(1+2)*((8-2)/(7-4))变成等价的后缀表达式。
例如,后缀表达式(abc*+def*/-)的求值
四、后缀表达式的求值的算法

利用栈来实现算术表达式求值的算法

利用栈来实现算术表达式求值的算法

利用栈来实现算术表达式求值的算法利用栈来实现算术表达式求值的算法算术表达式是指按照一定规则组成的运算式,包含数字、运算符和括号。

在计算机中,求解算术表达式是一项基本的数学运算任务。

根据算术表达式的性质,我们可以考虑利用栈这一数据结构来实现求值算法。

一、算法思路首先,我们需要明确一个重要概念——逆波兰表达式(ReversePolish notation)。

逆波兰表达式是一种没有括号的算术表达式,其运算规则是先计算后面的数字和运算符,再计算前面的数字和运算符。

例如,对于算术表达式“3+4*5-6”,其对应的逆波兰表达式为“3 45 * +6 -”。

那么,我们可以利用栈来实现将中缀表达式转化为逆波兰表达式的过程,具体步骤如下:1. 创建两个栈——操作数栈和操作符栈。

2. 从左到右扫描中缀表达式的每一个数字和运算符,遇到数字则压入操作数栈中,遇到运算符则进行如下操作:(1)如果操作符栈为空或当前运算符的优先级大于栈顶运算符的优先级,则将当前运算符压入操作符栈中。

(2)如果当前运算符的优先级小于或等于栈顶运算符的优先级,则将栈顶运算符弹出并加入操作数栈中,重复此过程直到遇到优先级较低的运算符或操作符栈为空为止,然后将当前运算符压入操作符栈中。

3. 扫描完中缀表达式后,若操作符栈不为空,则将其中所有运算符弹出并加入操作数栈中。

4. 最终,操作数栈中存放的就是逆波兰表达式,我们可以按照逆波兰表达式的计算规则来计算其结果。

二、算法优点利用栈来实现算术表达式求值的算法具有以下优点:1. 代码简洁易懂,易于实现和维护。

2. 由于将中缀表达式转化为逆波兰表达式后,可以减少运算符的优先级关系而消除括号,从而减少求值的复杂度,提高程序的执行效率。

三、代码实现下面是利用栈来实现算术表达式求值的算法的Python代码实现:```pythonclass Stack:def __init__(self):self.items = []def push(self, item):self.items.append(item)def pop(self):return self.items.pop()def peek(self):return self.items[-1]def is_empty(self):return len(self.items) == 0def size(self):return len(self.items)def calculate(op_num1, op_num2, operator):if operator == "+":return op_num1 + op_num2elif operator == "-":return op_num1 - op_num2elif operator == "*":return op_num1 * op_num2elif operator == "/":return op_num1 / op_num2def infix_to_postfix(infix_expr):opstack = Stack()postfix_expr = []prec = {"+": 1, "-": 1, "*": 2, "/": 2, "(": 0} token_list = infix_expr.split()for token in token_list:if token.isdigit():postfix_expr.append(token)elif token == '(':opstack.push(token)elif token == ')':top_token = opstack.pop()while top_token != '(':postfix_expr.append(top_token)top_token = opstack.pop()else:while (not opstack.is_empty()) and(prec[opstack.peek()] >= prec[token]):postfix_expr.append(opstack.pop())opstack.push(token)while not opstack.is_empty():postfix_expr.append(opstack.pop())return " ".join(postfix_expr)def postfix_eval(postfix_expr):opstack = Stack()token_list = postfix_expr.split()for token in token_list:if token.isdigit():opstack.push(int(token))else:op_num2 = opstack.pop()op_num1 = opstack.pop()result = calculate(op_num1, op_num2, token) opstack.push(result)return opstack.pop()infix_expr = "3 + 4 * 5 - 6"postfix_expr = infix_to_postfix(infix_expr)print(postfix_expr)print(postfix_eval(postfix_expr))```四、总结算术表达式求值是一项常见的数学运算任务,利用栈这一数据结构来实现求值算法是一种简单有效的方法,它将中缀表达式转化为逆波兰表达式后,可以消除括号并减少运算符的优先级关系,从而提高程序的执行效率。

逆波兰式探微——后缀表达式的求法及其适用情况比较

逆波兰式探微——后缀表达式的求法及其适用情况比较
维普资讯
科
科 苑论 谈 ljl
逆波 兰 式探微
后缀 表达式 的求法及其 适用情况 比较
郭 群

( 宁对外经贸学院 , 宁 大连 16 5 ) 辽 辽 10 2
摘 要: 介绍数据结构学科 中的一个重要领 域——后缀表达式 ( 波兰式 ) 逆 的求法。通过栈 的应用、 标识符树 、 号转换三种方 法讨论后缀表达 扩
式 实现 方 法 以及 它 们 的 适 用 范 围 。
关键词 : 后缀表 达式 ; ; 栈 标识符树 ; 号转换 扩
1概 述 符号栈 。
1 中缀表达式( o t n。 . 1 I N t i ) —般我们所 x ao 2 A遇到结束符“ ’ . 1 , 运算符 号栈 内的 则把 用表达式是将运算符号放在两个运算对象的 中 所有运算符号依次弹 出, 并压 ^ 出符号栈 。 输 间 , : bc 等等, 比如 a , d + / 我们把这样的式子称为 中 2 . 输人为 “” . 若 1 5 一 的单 目 运算符 , 改为 0 与 缀表达式。中 缀表达式在运算时既要考 虑括号的 运算对象在前, 运算符在后。 ’ 作用, 要考虑运算符的优先级 , 还要考虑运算符出 例如:3+4/ 5一6+1 ) 8# ( 2 ( 5) 前缀表达式 : A B * DE + G , — C + 一F H + 现的先后次序。因此各运算符实际的运算次序往 2 2后缀表达式求值 后§ 达式 : C + E F + 疆 AB 一 D + G H一 / 往同它们在表达式中出现的先后 次序是不一致 2 .后缀表达式求值用 到一个数栈和一个 2 1 4括号转换 的, 是不可预测的。 存放后缀表达式的字符型数组。其实现过程就是 求后缀表达式 的问题不但在计算机编译程 1 2前缀表达式(D o t n 。前缀表达 从头至尾扫描数组中的后缀表达式: P xN t i ) ao 序中十分有用 , 而且是数据结构学科、 各类计算机 式, 也称为波兰表达式 , 是为了纪念波兰数学家鲁 2 . 当遇到运算对象时就把它压人到数栈 升学考试,以及全国计算机等级考试和高级程序 . 21 1 卡谢维奇( nL ks wc) J u ai i 而命名的。 a e z 在前缀表达 中; 员考试中常考 的题型, 在理论考试中都需要 比 较 式中, 运算符出 现在两个运算对象( 操作数) 之前。 2. , 2当遇到运算符时,就执行两次出 的 多的时间。 2 1 栈  ̄: t C—A }B+D 的前经 为 : l l 舀 式 +一C 操作, 对出栈的数进行该运算符指定的运算 , 并把 下面介绍一种使用 园扩号转换求后缀表达 A 。 B D 计 的 算 结果压人 膨盹 。 蛰 式的方法 , 在理论考试中可以方便地将中缀表达 1 3后缀表达式(otxN tt n。后缀表达 P si o i ) f ao 2 2 3重复 2 . 、.1 , 1 .1 2 . 直至扫描到表 达式 式转换为后缀表达式。 2. 2. 1 2 式规定把运算符放在两个运算对象的后面。在后 的终止符‘” 辞, 在数栈的栈顶得到表达式的值。 41 .转换步骤 缀表达式 中, 不存在运算符的优先级 问题, 也不存 【 】 例 后缀表达式 3 56 1 42 5+一/ 8} 4 ,将 中缀表达式根据运算先后次序全部 1 1 在任何括号 , 计算 的顺序完全按照运算符 出现的 + ̄ - t - g过程 : ' 用圆扩号扩起来; 先后次序进行。 比中缀表达式的求值要简单得多。 3 56 5十一/8}+ 42 1 61 + 5的结果为:1 2 4 .移动所有运算符 号来取代所有的右括 J 2 比如中缀表达式 :— * + , C A B D 其后缀表达式 号, 以最近为原则进行替换; 并 为 : C A B}一D +。 2— 1 5 2 的结果为 : 4 4 .删除所有的左括弧 ,即得到后缀表达 . 1 3 由于前缀表达式称为波兰表达式 , 所以后缀 式。 表达式也称为逆波兰式。 44的结果为 : / 1 【 将( 例】 A—B+C—D E转换为后缀表 ) 通常编译器在处理运算 时先要将中缀表达 达式 为 A B—C+D E } 一 式转换为后缀表达式然后再进行运算。 1 8的结果为 : " 8 使用括 号转换法 的转换过程和方法十分简 2栈 的应 用 单。 掌握这种方法, 对付理论考试中关于求后缀表 21 .转换方法。转换过程需要两个栈 : —个运 3 8的结果为:1 达式的棚念 陛的试题是十分有效的, + 1 且不易出 错。 算符号栈和—个后缀表达式输出符号栈。 3标识符树 如果用园括号转换法编写程序 , 则其效率却 2. 人 1 读 操作数 , 1 直接送输出符号栈。 将算术表达式用二叉树来表示 , 称为标识符 是非常低。 因是计算机在处理中 原 缀表达式时. 必 2 .读 人 . 1 2 运算符 , 入 压 运算 号 。 }栈 树, 也称为二叉表示树 。 须对表达式字符串进行两次扫描: z 21 1 若后进的运算符优先级高于先进的 , 3 标识符树的特点 : . 1 第一次扫描先读取整个表达式 , 并依据步骤 则继续进栈; 3 .运算对象 ( .1 1 标识符) 都是叶结点。 41 插入括号 ; .1 . 2 . 若 后进 的运算符优先级不高于先进 .. 12 2 3 .运算符都是根结点 。 . 1 2 第二次扫描是为 了移动运算符 以取代一个 的, 则将运算符号栈内高于或等于后进运算符级 3 .对算术表达式 的标识符树进行后序遍 适当的右括号, . 1 3 如步骤 4 . .2 1 别的 运算符依次弹出后, 自己进栈。 历, 能方便地得至原算术表达式的后缀表达式。 J 『 最后再删 除全部左括号 , 如步骤 4 . 才 能 . , 1 3 21 .3括号处理 : . 3 从表达式产生标识符树的方法: . 2 完或整个转换过程。 2 .1 . .遇到开括号“”进运算符号栈 ; 1 3 (, 3. .1 2 读人表达式的一部分产 生相应 二叉树 5结 论 2 1 2 后 , .. 3 再读人运算符时, 与二叉树根结点的运算符 比 5 若单纯 的求后缀表达式 , 以第一种方 . 1 则 遇 到闭 括 较优先级的高低: 2 .读人优先级高于根结点的 法 , 3 .1 .1 即利用堆栈求后缀表达式的方法为好。 号 “”则 优先级 , ), 则读人的运算符作为根的右子树 , 原来二 5 2若同时求表达式的前缀表达式和后缀表 把最 靠 近 叉树的右子 ,成为读人运算符的左子树;2 2 达式的话, 以 树 3, 1 则 利用标识符树的方法为好。 它的特 的开 括号 读人优先级不高 ( 等于或低于根结点的优先级 ) 点是求得表达式 的标识符树 以后,其前序遍历的 。 “” ( 。以及 则读人运算符作为树根,而原来二叉树作为它的 序列 即前缀表达式 ; 而后序遍历 的 序列即后缀表 h 1 L 4 6 6 .2* + 黻 其 后进 栈 左子树 ; 达式。 I ^ L1 2 #出 的运算 符 3 .遇到括号先使括号 内的表达式产生一 . 2 2 5 若只是为 了 _ 3 解答考题中出现的求后缀表 L l 1 L 一 2 1 量) , : 2十 《 ■出 依 次弹 出 棵二叉树 , 再把它的根结点连到前面已产生的二 达式的结果 , 符日 而不考虑如何用程序去实现的话 , 则 l , 1 1 一 遇) 冼弹 * 1十 《 , 到输 出符 叉 树根结点的右子树 E 去。 符日 使用圆括号转换法求后缀表达式的方法无疑是最 } 地 41 1 / ■出 ^ 目 十^ 螂 ≈琏 埔 挠 8 ^ 2* 1 6 1 h B 号栈 , 但 3 应用举例 . 3 佳的。 ● 2i^ 1 ^ 1 l * , ■ 括号 均不 【 】 出(+B c )( + (+ — )的 例 画 A (— ) ( E FG H ) /D 参 考文 献 压 人 输 出 标识符树 , 并求它的前序序列和后序序列。 [ 1 ]陈元春 , 张亮 , 勇. 王 实用 ( 下转 2 2页 ) 6

后缀表达式求值

后缀表达式求值

后缀表达式求值实验报告书课程名:数据结构题⽬:后缀表达式求值1、实验题⽬(1)掌握栈“后进先出”的特点。

(2)掌握栈的典型应⽤——后缀表达式求值。

2、实验内容(1)⽤键盘输⼊⼀个整数后缀表达式(操作数的范围是0~9,运算符只含+、—、*、/、,⽽且中间不可以有空格),使⽤循环程序从左向右读⼊表达式。

(2)如果读⼊的是操作数,直接进⼊操作数栈。

(3)如果读⼊的是运算符,⽴即从操作数栈取出所需的操作数,计算操作数运算的值,并将计算结果存回操作数栈。

(4)检验程序运⾏结果。

3、实验要求(1)分析后缀表达式求值的算法思想,⽤C(或C++)语⾳设计程序。

(2)上机调试通过实验程序。

(3)给出具体的算法分析,包括时间复杂度和空间复杂度等。

(4)撰写实验报告(把输⼊实验数据及运⾏结果⽤抓图的形式粘贴到实验报告上)。

(5)本程序运⾏结果通过以后,添加到原教材验证性实验3的菜单中去。

4、实验步骤与源程序错误!未找到引⽤源。

实验步骤使⽤循环从左到右输⼊后缀表达式。

如输⼊的是运算符,⽴即从操作数栈取出两个操作数,计算上述操作数和运算符的值,然后存回操作数栈。

如输⼊的是操作数则直接存⼊操作数栈。

整个过程只需要⼀个操作数栈。

由⼀个链栈结点定义node、push()、pop()、empty()运算函数⼀个main()主函数(完成数据输⼊和函数调⽤)、两个个功能函数:isoperator()//判运算符getvalue()//计算表达式值错误!未找到引⽤源。

源代码#include#includestruct node // 栈结构声明{int data; // 数据域struct node *next; // 指针域typedef struct node stacklist; // 链表新类型typedef stacklist *link; // 链表指新针类型link operand=NULL; // 操作数栈指针void push( int value) // 进栈,存⼊数据{link newnode; // 新结点指针newnode=new stacklist; // 分配新结点if (!newnode){printf("分配失败!"); // 存⼊失败return ;}newnode->data=value; // 创建结点的内容newnode->next=operand;operand=newnode; // 新结点成为栈的开始return ;}void pop(int *value) // 出栈,取出数据{link top; // 指向栈顶if (operand !=NULL){top=operand; // 指向栈顶operand=operand->next; // 移动栈顶指针,指向下⼀个结点*value=top->data; // 取数据delete top; // 吸收结点}else*value=-1;}int empty() // 判栈空{if (operand!=NULL) return 1;else return 0;int isoperator(char op) // 判运算符{switch (op){case'+':case'-':case'*':case'/': return 1; // 是运算符,返回default: return 0; // 不是运算符,返回}}int getvalue(int op,int operand1,int operand2) // 计算表达式值{switch((char)op){case'*': return(operand1*operand2);case'/': return(operand1/operand2);case'+': return(operand1+operand2);case'-': return(operand1-operand2);}}void main() // 主函数{char exp[100];int operand1=0; // 定义操作数int operand2=0; // 定义操作数int result=0; // 定义操作结果变量int pos=0; // ⽬前表达式位置printf("\t\n 请输⼊后缀表达式:");gets(exp); //cin.getline(exp,81) // 读取后缀表达式 printf("\t\n\n 后缀表达式[%s]的结果是:",exp); while (exp[pos] !='\0' && exp[pos] !='\n') // 分析表达式字符串{if (isoperator(exp[pos])) // 是运算符,取两个操作数{pop(&operand2);pop(&operand1);push(getvalue(exp[pos],operand1,operand2)); }elsepush(exp[pos]-48); // 是操作数,压⼊操作数栈 pos++; // 移到下⼀个字符串位置}pop(&result); // 弹出结果printf("%d\n",result); // 输出}5、测试数据与实验结果(可以抓图粘贴)图1:程序运⾏结果6、结果分析与实验体会结果分析:本次实验结果准确。

C语言下表达式的自动计算(两种方式)(报告+源代码)

一、设计思想第一种算法:将中缀表达式转为后缀表达式,然后通过后缀表达式计算出算术表达式的结果。

核心思想:第一步:中缀变后缀。

首先,我们做出一个统一的Node结构体,结构体内部包含四个属性,分别是操作符的字符‘op’,char类型;操作符的优先级‘level’,int 类型;数字的浮点数数值‘od’,float类型;Node的标识符,int类型。

然后,定义一个Node结构体类型的数组*listNode,这里的*listNode用的是全局变量,为了方便在得到后缀表达式后,不需再传递给计算的方法。

定义一个存放操作符的栈,遍历用户输入的算术表达式(不考虑错误情况),在遍历的过程中如果遇到数字,直接将数字存放在*listNode里面;如果遇到了操作符,则判断操作符栈目前是不是为空,如果为空,直接将遇到的操作符放入操作符栈中,如果操作符栈不为空,那么观察操作符栈中栈顶的操作符,然后再次判断当前遇到的操作符的优先级是不是比栈顶的操作符的优先级高,如果是,那么将当前的操作符入操作符栈;如果不是,那么将操作符栈的栈顶操作符取出,追加到*listNode中,然后继续观察栈顶操作符,直到当前的操作符的优先级比栈顶操作符的优先级高或者操作符栈为空时,将当前操作符入操作符栈。

如果遇到了左括号,那么定义其优先级为最低,然后直接将左括号入操作符栈。

如果遇到了右括号,那么开始从操作符栈中取出操作符追加到*listNode中,直到遇到了与之对应的左括号,然后将左括号和右括号一起销毁。

当遍历完成了算术表达式之后,这时判断操作符栈是否为空,如果不为空,那么从操作符栈中依次取出栈顶操作符追加到*listNode中,直到操作符栈为空,那么就代表我们将中缀表达式转变成为了后缀表达式。

第二步:通过得到的后缀表达式,计算算术表达式。

首先,定义一个数字栈用来存放数值。

然后,遍历*listNode中的每一个Node,如果Node是一个数字,那么就将数字入数字栈,如果Node是一个操作符,那么就从数字栈中依次取出栈顶的两个数字,然后根据操作符计算这两个数字,将得到的结果再次入数字栈,直到遍历*listNode完成,最终数字栈中会只剩下一个Node,那就是我们计算出算术表达式的结果,将结果返回给main 函数用来输出。

中缀表达式转后缀表达式并计算结果(c语言版)

中缀表达式转后缀表达式中缀表达式转后缀表达式的规则。

1.遇到操作数:直接输入到后缀表达式栈2.遇到运算符,直接入操作符栈3.遇到左括号:直接将其入栈4.遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出。

5.遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈6.最终将操作符栈中的元素依次出栈,输出到后缀表达式栈。

以下是自己写的代码。

亲测没有问题。

(模拟一个计算器,可以带括号,中间可以空格,只支持整数输入,但是输出结果精确到小数后6位)#include "stdio.h"#define MAX_LEN 100typedef struct cal{unsigned char isOper;//是否是操作数1,操作符0.操作数double Num; //值。

或者是操作符的ASCII值}STRUCT_CAL;#define IS_NUM 0x00#define IS_OPER 0x01STRUCT_CAL stackCal[MAX_LEN];STRUCT_CAL stackCalBack[MAX_LEN];unsigned char topCal;char stackOper[MAX_LEN];unsigned char topOper;/****************************************************************** 堆栈初始化*****************************************************************/void stackInit(void){int i;for(i=0;i<MAX_LEN;i++){stackCal[i].isOper = 0;stackCal[i].Num = 0;stackOper[i] = 0;}topCal = topOper = 0;}/***************************************************************** * 返回堆栈的栈顶,返回后栈顶减一*****************************************************************/ STRUCT_CAL * stackCalPop(void){if(topCal == 0)return (STRUCT_CAL *)0;return stackCal+(--topCal);}/***************************************************************** * 计算表达式入栈*****************************************************************/void stackCalPush(double num, unsigned char isOper){if(topCal>=MAX_LEN)return;stackCal[topCal].Num = num;stackCal[topCal].isOper= isOper;topCal++;}/***************************************************************** * 操作符出栈*****************************************************************/char stackOperPop(void){if(topOper == 0)return 0;return stackOper[--topOper];}/****************************************************************** 操作符入栈*****************************************************************/void stackOperPush(char oper){if(topOper >=MAX_LEN)return;stackOper[topOper++] = oper;}/****************************************************************** 比较两个sour sour1 的优先级* 1 sour >= sour1 直接入操作符栈* 0 sour < sour1 直接入计算表达式栈*****************************************************************/ unsigned char comparPrior(char sour, char sour1){if(sour =='\0' ||sour1 == '\0') return 1;switch(sour){case '+':case '-':if(sour1 == '*' ||sour1 == '/'||sour1 == '+' ||sour1 == '-' ){return 0;}else{return 1;}break;case '*':case '/':if(sour1 == '*' ||sour1 == '/'||sour1 == '+' ||sour1 == '-'||sour1 == '('){return 1;}else{return 0;}break;default:return 1;break;}}/****************************************************************** 将输入的字符串转换为栈*****************************************************************/void StrToCal(char *pStr){int tmpNum = 0;char tmpOper;char islastNum = 0;//判断上一个字符是什么。

栈的应用——表达式求值

栈的应⽤——表达式求值 表达式求值是程序设计语⾔编译中的⼀个基本问题,它的实现就是对“栈”的典型应⽤。

本⽂针对表达式求值使⽤的是最简单直观的算法“算符优先法”。

本⽂给出两种⽅式来实现表达式求值,⽅式⼀直接利⽤中缀表达式求值,需要⽤到两个栈,操作数栈和操作符栈。

⾸先置操作数栈为空栈,操作符栈仅有“#”⼀个元素。

依次读⼊表达式中的每个字符,若是操作数则进操作数栈,若是操作符则和操作符栈的栈顶运算符⽐较优先权作相应操作,直⾄整个表达式求值完毕。

⽅式⼆⾸先把中缀表达式转换为后缀表达式并存储起来,然后利⽤读出的后缀表达式完成求值,其本质上是⽅式⼀的分解过程。

表达式求值的代码如下:#include <iostream>#include "stack"#include "map"using namespace std;/* 只能求⼀位整数的加减乘除混合运算 */map<char, pair<int, int>> priority; // 存放各个操作符的栈内栈外优先级,first是栈内,second是栈外char infix[50]; // 存放初始的中缀表达式char postfix[50]; // 存放转化的后缀表达式int result;void MakePriority() // 构造运算符优先级表{priority.insert(make_pair('#', make_pair(0, 0))); // isp(#)=0, icp(#)=0priority.insert(make_pair('\n', make_pair(0, 0))); // isp(\n)=0, icp(\n)=0 表达式结尾的'#'⽤'\n'代替,这样可以省略表达式末尾的结束符'#'priority.insert(make_pair('(', make_pair(1, 6))); // isp(()=1, icp(()=6priority.insert(make_pair('*', make_pair(5, 4))); // isp(*)=5, icp(*)=4priority.insert(make_pair('/', make_pair(5, 4))); // isp(/)=5, icp(/)=4priority.insert(make_pair('%', make_pair(5, 4))); // isp(%)=5, icp(%)=4priority.insert(make_pair('+', make_pair(3, 2))); // isp(+)=3, icp(+)=2priority.insert(make_pair('-', make_pair(3, 2))); // isp(-)=3, icp(-)=2priority.insert(make_pair(')', make_pair(6, 1))); // isp())=6, icp())=1}void InfixToPostfix() // 把中缀表达式转换为后缀表达式{int i = 0;stack<char> optrStack; // 操作符栈char optr; // optr为栈顶的操作符optrStack.push('#');while (!optrStack.empty()){if (isdigit(infix[i])) // 是操作数则直接输出(追加到postfix结尾){postfix[strlen(postfix)] = infix[i];postfix[strlen(postfix) + 1] = '\0';i++; // 读⼊中缀表达式的下⼀个字符}else// 是操作符, ⽐较优先级{optr = optrStack.top(); // 取出栈顶操作符if (priority[infix[i]].second > priority[optr].first) // icp(infix[i]) > isp(optr),infix[i]⼊栈{optrStack.push(infix[i]);i++;}else if (priority[infix[i]].second < priority[optr].first)// icp(infix[i]) < isp(optr),optr退栈并输出{postfix[strlen(postfix)] = optr;postfix[strlen(postfix) + 1] = '\0';optrStack.pop();}else// icp(infix[i]) = isp(optr),退栈但不输出,若退出的是'(',则继续读⼊下⼀个字符{optrStack.pop();if (optr == '(')i++;}}}}void CalculateByPostfix() // 通过后缀表达式求值{int i = 0;stack<int> opndStack; // 操作数栈int left, right; // 左右操作数int value; // 中间结果int newOpnd;while (postfix[i] != '#' && i < strlen(postfix)){switch (postfix[i]){case'+':right = opndStack.top(); // 从操作数栈中取出两个操作数opndStack.pop();left = opndStack.top();opndStack.pop();value = left + right;opndStack.push(value); // 中间结果⼊栈break;case'-':right = opndStack.top();opndStack.pop();left = opndStack.top();opndStack.pop();value = left - right;opndStack.push(value);break;case'*':right = opndStack.top();opndStack.pop();left = opndStack.top();opndStack.pop();value = left * right;opndStack.push(value);break;case'/':right = opndStack.top();opndStack.pop();left = opndStack.top();opndStack.pop();if (right == 0){cerr << "Divide by 0!" << endl;}else{value = left / right;opndStack.push(value);}break;default:newOpnd = (int)(postfix[i] - 48); // 操作数直接⼊栈opndStack.push(newOpnd);break;}i++;}result = opndStack.top();}void CalculateByInfix() // 直接利⽤中缀表达式求值{int i = 0;stack<char> optrStack; // 操作符栈stack<int> opndStack; // 操作数栈char optr; // optr为操作符栈顶的操作符int left, right, value; // 左右操作数以及中间结果optrStack.push('#');optr = optrStack.top();while (!optrStack.empty()) // 直到操作符栈为空{if (isdigit(infix[i])) // 是操作数, 进操作数栈{value = (int)(infix[i] - 48);opndStack.push(value);i++;}else// 是操作符, ⽐较优先级{optr = optrStack.top(); // 取出操作符栈顶的操作符if (priority[infix[i]].second > priority[optr].first) // icp(infix[i]) > isp(optr),infix[i]⼊栈 {optrStack.push(infix[i]);i++;}else if (priority[infix[i]].second < priority[optr].first) // icp(infix[i]) < isp(optr),optr退栈并输出{optrStack.pop();right = opndStack.top(); // 从操作数栈中取出两个操作数opndStack.pop();left = opndStack.top();opndStack.pop();switch (optr){case'+':value = left + right;opndStack.push(value); // 中间结果⼊栈break;case'-':value = left - right;opndStack.push(value); // 中间结果⼊栈break;case'*':value = left * right;opndStack.push(value); // 中间结果⼊栈break;case'/':if (right == 0){cerr << "Divide by 0!" << endl;}else{value = left / right;opndStack.push(value);}break;default:break;}}else{optrStack.pop();if (optr == '(')i++;}}}result = opndStack.top();}int main(){MakePriority(); // 构造运算符优先级表cout << "请输⼊中缀表达式:";cin >> infix;cout << "直接利⽤中缀表达式求值为:";CalculateByInfix();cout << result << endl;cout << "转化为后缀表达式:";InfixToPostfix();for (int i = 0;i < strlen(postfix);i++){cout << postfix[i];}cout << endl;cout << "利⽤后缀表达式求值为:";CalculateByPostfix();cout << result << endl;return0;} 为了⽅便起见,本⽂只是简单的设计了⼀个针对⼀位整数的四则运算进⾏求值的算法,对于处理多位整数的四则运算,需要对本⽂接受输⼊的数据类型进⾏“升阶”,把字符数组换成字符串数组,将⼀个整数的多位数字存⼊⼀个字符串进⾏处理。

栈和队列的应用场景

栈和队列的应用场景栈和队列是数据结构中常见的两种基本数据结构,它们在实际生活和计算机领域中有着广泛的应用场景。

本文将从实际应用的角度出发,介绍栈和队列在不同场景下的具体应用。

### 一、栈的应用场景#### 1.1 浏览器的后退和前进功能在浏览器中,当我们访问一个网页时,浏览器会将该网页的 URL 存储在一个栈中。

当我们点击后退按钮时,浏览器会从栈顶取出上一个网页的 URL,实现后退功能;当我们点击前进按钮时,浏览器会从栈中取出下一个网页的 URL,实现前进功能。

#### 1.2 括号匹配在编程中,栈常用于检查表达式中的括号是否匹配。

当遇到左括号时,将其入栈;当遇到右括号时,将栈顶元素出栈并与右括号进行匹配。

如果匹配成功,则继续;如果匹配失败,则表达式中存在不匹配的括号。

#### 1.3 撤销操作在文本编辑器或图像处理软件中,撤销操作通常使用栈来实现。

每次编辑操作都会将编辑内容存储在栈中,当用户点击撤销按钮时,软件会从栈中取出上一个编辑操作,实现撤销功能。

### 二、队列的应用场景#### 2.1 系统任务调度在操作系统中,队列常用于实现任务调度。

操作系统会将需要执行的任务按照先来先服务的原则排入队列,然后逐个执行。

这种方式可以保证任务的顺序性和公平性。

#### 2.2 打印队列在打印机中,打印任务通常按照先后顺序排入打印队列中,然后依次执行。

这样可以避免多个打印任务同时请求打印,导致打印机发生冲突。

#### 2.3 消息队列在分布式系统中,消息队列被广泛应用于解耦和异步处理。

生产者将消息发送到队列中,消费者从队列中取出消息并进行处理,实现了生产者和消费者之间的解耦。

### 三、栈和队列的综合应用场景#### 3.1 模拟计算器在计算器的设计中,可以使用栈来实现表达式的计算。

将中缀表达式转换为后缀表达式,然后利用栈来计算后缀表达式的值,实现计算器的功能。

#### 3.2 资源分配在操作系统中,可以使用队列来实现资源的分配。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
相关文档
最新文档