(编译原理)逆波兰式算法地源代码
- 格式:doc
- 大小:61.00 KB
- 文档页数:10
今天用栈编写了一个后缀表达式(逆波兰式求值)求值的程序。
在书中栈的基本类型是int型,我这里用char类型实现。
所以操作数是有限制的。
代码如下:#include<iostream>#include<string>using namespace std;const int baseSize=30,reSize=10;typedef char elem;typedef struct{elem *base;int top;int stacksize;}SeqStack;void InitStack(SeqStack & S,int maxsize){if(maxsize<=0)maxsize=baseSize;S.base=new char[maxsize];if(!S.base){cout<<"memory allocation failure\n";exit(1);}S.stacksize=maxsize;S.top=0;}bool stacktop(SeqStack & S,char &e){if(!S.top) return false;e=S.base[S.top-1];return true;}bool push(SeqStack &S ,char e)if(S.top>=S.stacksize){cout<<"stack is full\n";return false;}S.base[S.top++]=e;return true;}bool pop(SeqStack & S,char& e) {if(!S.base) return fal se;e=S.base[--S.top];return true;}void get_PolishList(char *a){char m_strPolish[50];gets(m_strPolish);strcpy(a,m_strPolish);}int getvalue(char op,char a,char b) {switch(op){case'+':return (a-'0')+(b-'0');case'-':return (a-'0')-(b-'0');case'*':return (a-'0')*(b-'0');case'/':return (a-'0')/(b-'0');}}int main(void){SeqStack Polish;InitStack(Polish,40);char m_strPolish[60];get_PolishList(m_strPolish);for(int i=0;i<strlen(m_strPolish);i++){if(m_strPolish[i]<'9'&&m_strPolish[i]>'0')push(Polish,m_strPolish[i]);else{char var1,var2;pop(Polish,var1);pop(Polish,var2);push(Polish,getvalue(m_strPolish[i],var2,var1)+'0');}}int result=0;char value;pop(Polish,value);cout<<"Result: "<<value-'0'<<endl;// cout<<m_strPolish<<endl;return 250;}测试数据是:3*5+(6-8/4)*7 转换成后缀表达式35*684/-7*+;结果如图:。
用栈进行表达式求值并输出逆波兰式(源代码)#include "stdio.h"#include "malloc.h"#include "string.h"#define STACK_INIT_SIZE 100#define STACKINCREMENT 10typedef struct{char *top,*base;int csize;}*charstack,cstack;typedef struct{float *top,*base;int fsize;}*floatstack,fstack;charstack initcharstack(){charstack s;s=(charstack)malloc(sizeof(cstack));s->base=s->top=(char*)malloc(STACK_INIT_SIZE * sizeof(char));s->csize=STACK_INIT_SIZE;return s;}floatstack initfloatstack(){floatstack s;s=(floatstack)malloc(sizeof(fstack));s->base=s->top=(float*)malloc(STACK_INIT_SIZE * sizeof(float));*(s->top)=-1;s->fsize=STACK_INIT_SIZE;return s;}char chargettop(charstack s){return *(s->top-1);}float floatgettop(floatstack s){return *(s->top-1);}void charpush(charstack s,char c){if((s->top-s->base)>=s->csize){s->base =(char *)realloc(s->base ,(s->csize +STACKINCREMENT)*sizeof(char));s->top=s->base +s->csize ;s->csize +=STACKINCREMENT;}*s->top++=c;}void floatpush(floatstack s,float a){if((s->top-s->base)>=s->fsize){s->base =(float *)realloc(s->base ,(s->fsize +STACKINCREMENT)*sizeof(float));s->top=s->base+s->fsize ;s->fsize +=STACKINCREMENT;}*s->top++=a;}char charpop(charstack s){char c;c=*(s->top-1);s->top--;return c;}float floatpop(floatstack s){float a;a=*(s->top-1);s->top--;return a;}int in(char c){char s[7]={'+','-','*','/','(',')','#'};int i;for(i=0;i<7;i++)if(s[i]==c) return 1;return 0;}float operate(float a,char c,float b){switch(c){case '+':return (a+b);case '-':return (a-b);case '*':return (a*b);case '/':return (a/b);default :return 0.0;}}int charstackempty(charstack s){if(s->base==s->top)return 1;return 0;}char precede(char c1,char c2){char s[7]={'+','-','*','/','(',')','#'};char str[7][7]={'>','>','<','<','<','>','>','>','>','<','<','<','>','>','>','>','>','>','<','>','>','>','>','>','>','<','>','>','<','<','<','<','<','=',' ','>','>','>','>',' ','>','>','<','<','<','<','<',' ','='};int i=0,j=0;while(s[i]!=c1)i++;while(s[j]!=c2)j++;return str[i][j];}float evaluateexpression(char *str){charstack optr;floatstack opnd;char c1;float a,b;int i=0;optr=initcharstack();opnd=initfloatstack();charpush(optr,'#');while(str[i]!='#'||chargettop(optr)!='#'){if(!in(str[i])){floatpush(opnd,str[i]-48.0);i++;}else{if(chargettop(optr)=='#'){charpush(optr,str[i]);i++;}elseswitch(precede(chargettop(optr),str[i])){case '<':charpush(optr,str[i]);i++;break;case '=':c1=charpop(optr);i++;break;case '>':c1=charpop(optr);a=floatpop(opnd);b=floatpop(opnd);floatpush(opnd,operate(b,c1,a));break;}}}printf("\n");return floatgettop(opnd);}char* Ipn(char *s){charstack s1,s2;int length=0;char *p=s,ch,*result;s1=initcharstack();s2=initcharstack();charpush(s2,'#');for( ;*p!='#';p++){switch(*p){case '(':charpush(s2,*p);break;case ')':while(chargettop(s2)!='('){ch=charpop(s2);charpush(s1,ch);}ch=charpop(s2);break;case '+':case '-':for(ch=chargettop(s2);ch!='#';ch=chargettop(s2)){if(ch=='(')break;else{ch=charpop(s2);charpush(s1,ch);}}charpush(s2,*p);length++;break;case '*':case '/':for(ch=chargettop(s2);ch!='#'&&ch!='+'&&ch!='-';ch=chargettop(s2)){ if(ch=='(')break;else{ch=charpop(s2);charpush(s1,ch);}}charpush(s2,*p);length++;break;default:charpush(s1,*p);length++;}}while(!charstackempty(s2)&&chargettop(s2)!='#'){ch=charpop(s2);charpush(s1,ch);}result=(char*)malloc(sizeof(char)*(length+1));result+=length;*result='\0';result--;for( ;!charstackempty(s1);result--){ch=charpop(s1);*result=ch;}++result;return result;}void main(){float r;char str[100],*s;printf("Input one expression ending with #:\n\t");gets(str);s=Ipn(str);printf("The Reverse Polish Nation of the expression is:\n\t%s",s);printf("\n");r=evaluateexpression(str);printf("The result is %f.\n\n",r);}。
逆波兰表达式计算 java逆波兰表达式是一种将运算符写在操作数之后的表示方法,计算逆波兰表达式可以使用栈来实现。
下面是一个使用Java实现逆波兰表达式计算的例子:```javaimport java.util.Stack;public class RPNCalculator {public static double evaluateRPN(String[] tokens) {Stack<Double> stack = new Stack<>();for (String token : tokens) {if (isOperator(token)) {double operand2 = stack.pop();double operand1 = stack.pop();double result = calculate(token, operand1, operand2);stack.push(result);} else {stack.push(Double.parseDouble(token));}}return stack.pop();}private static boolean isOperator(String token) {return token.equals("+") || token.equals("-") ||token.equals("*") || token.equals("/");}private static double calculate(String operator, double operand1, double operand2) {switch (operator) {case "+":return operand1 + operand2;case "-":return operand1 - operand2;case "*":return operand1 * operand2;case "/":return operand1 / operand2;default:throw new IllegalArgumentException("Unknown operator: " + operator);}}public static void main(String[] args) {String[] tokens = {"2", "1", "+", "3", "*"};double result = evaluateRPN(tokens);System.out.println("Result: " + result);String[] tokens2 = {"4", "13", "5", "/", "+"};double result2 = evaluateRPN(tokens2);System.out.println("Result: " + result2);}}```这个例子演示了如何使用栈来计算逆波兰表达式。
Java实现《编译原理》中间代码⽣成-逆波兰式⽣成与计算-程序解析Java 实现《编译原理》中间代码⽣成 -逆波兰式⽣成与计算 - 程序解析编译原理学习笔记(⼀)逆波兰式是什么?逆波兰式(Reverse Polish notation,RPN,或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后)⼀般的表达式⼜称中缀表达式,这种表达式的⼆元运算符放在两个运算量之间。
⽽逆波兰表达式⼜称后缀表达式,这种表达式把运算符放在运算量后⾯。
⽐如如 a+b 的逆波兰式表⽰为 ab+注意:逆波兰式是⼀个⽆括号表达式;逆波兰式的运算符出现的顺序就是原表达式的运算顺序。
(⼆)逆波兰式编译原理有什么关系?逆波兰式,三元式,四元式等是编译原理 - 中间代码⽣成阶段的常见的中间代码形式。
(三)本篇任务通过设计,使⽤ Java 语⾔编写⼀个逆波兰式⽣成程序,测试效果:(四)Java 源代码package com.java997.analyzer.rpn;import java.util.HashMap;import java.util.Map;import java.util.Scanner;import java.util.Stack;/*** <p>* 逆波兰式** @author XiaoPengwei* @since 2019-06-19*/public class RpnMain {/*** 检查算术表达术括号是否匹配, 语法是否正确** @param s 算术表达术* @return boolean*/public boolean isMatch(String s) {//括号符号栈Stack<Character> charStack = new Stack<>();//将表达式的字符串转换成数组char[] charArray = s.toCharArray();//遍历数组for (char aChar : charArray) {if (aChar == '(') {charStack.push(aChar);} else if (aChar == ')') {//如果是 ) , 且栈为空则返回 falseif (charStack.isEmpty()) {return false;} else {//如果是 ) , 且栈不为空则返回 false//peek() 是返回栈顶的值, 不做其他操作if (charStack.peek() == '(') {//把栈顶的值删除charStack.pop();}}}}//⾛到这⾥, 栈为空则表达式正确return charStack.empty();}/*** 判断是否为操作符 + - * /** @param charAt* @return boolean*/public boolean isOperator(char charAt) {return charAt == '+' || charAt == '-' || charAt == '*' || charAt == '/'; }/*** 根据正确的表达式, 获取逆波兰式** @param input* @return ng.String*/public StringBuilder getRpn(String input) {//结果StringBuilder sb = new StringBuilder();sb.append("The RPN is: ");//运算符栈Stack<Character> opStack = new Stack();//运算符优先级Map<Character, Integer> opMap = new HashMap(5);opMap.put('(', 0);opMap.put('+', 1);opMap.put('-', 1);opMap.put('*', 2);opMap.put('/', 2);//处理字符串for (int i = 0; i < input.length(); i++) {//如果是'('直接压栈if (input.charAt(i) == '(') {opStack.push('(');} else if (new RpnMain().isOperator(input.charAt(i))) {//如果是运算符char curOp = input.charAt(i);//如果运算符栈是空,就直接压栈if (opStack.isEmpty()) {opStack.push(curOp);} else if (opMap.get(curOp) > opMap.get(opStack.peek())) {//运算符栈不为空,且当当前运算符的优先级⽐站内第⼀个运算符的优先级⾼的时候,压栈 opStack.push(curOp);} else {//栈不为空,且运算符的优先级⼩于等于栈顶元素for (int j = 0; j <= opStack.size(); j++) {//弹出栈内第⼀个元素char ch = opStack.pop();sb.append(ch);if (opStack.isEmpty()) {opStack.push(curOp);break;} else if (opMap.get(curOp) > opMap.get(opStack.peek())) {opStack.push(curOp);break;}}}} else if (input.charAt(i) == ')') {//如果是')'就把站内'('上的元素都弹出栈for (int j = 0; j < opStack.size(); j++) {char c = opStack.pop();if (c == '(') {break;} else {sb.append(c);}}} else if ('A'<=input.charAt(i)&&input.charAt(i)<='Z'){//如果是字母就直接添加sb.append(input.charAt(i));}else if ('a'<=input.charAt(i)&&input.charAt(i)<='z'){//如果是字母就直接添加sb.append(input.charAt(i));}else if (Character.isDigit(input.charAt(i))){//如果是数字sb.append(input.charAt(i));}else {return new StringBuilder("But the expression contains unrecognizable characters");}}//把栈内剩余的运算符都弹出站for (int i = 0; i <= opStack.size(); i++) {sb.append(opStack.pop());}return sb;}public static void main(String[] args) {RpnMain rpnMain = new RpnMain();Scanner sc = new Scanner(System.in);while (true) {System.out.println("==========================\nPlease input an expression:");String input = sc.nextLine();if ("q".equals(input)) {sc.close();return;} else {if (rpnMain.isMatch(input)) {System.out.println("The expression's brackets are matched");// 获取逆波兰式System.out.println(rpnMain.getRpn(input));} else {System.out.println("Error: The expression's brackets are not matched! Enter 'q' to exit");}}}}}测试:。
中国计量学院《编译原理设计》课程论文题目:中缀表达式的逆波兰表示学生姓名:学号:学生专业:班级:二级学院:一、摘要编译原理是计算机科学与技术专业最重要的一门专业基础课程,内容庞大,涉及面广,知识点多。
由于该课程教、学难度都非常大,往往费了大量时间而达不到预期教学效果俗语说:学习的最好方法是实践。
本课程设计正是基于此,力求为学生提供一个理论联系实际的机会,通过布置一定难度的课题,要求学生独立完成。
我们这次课程设计的主要任务是编程实现对输入合法的中缀表达式进行词法分析、语法分析,构造相应的逆波兰式,计算后缀表达式的值输出结果。
逆波兰式也叫后缀表达式,即将运算符写在操作数之后。
通过实践,建立系统设计的整体思想,锻炼编写程序、调试程序的能力,学习文档编写规范,培养独立学习、吸取他人经验、探索前言知识的习惯,树立团队协作精神。
同时,课程设计可以充分弥补课堂教学及普通实验中知识深度与广度有限的缺陷,更好地帮助学生从全局角度把握课程体系。
关键字:逆波兰式;语法分析;中缀表达式二、实验综述在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。
对中缀表达式的计值,并非按运算符出现的自然顺序来执行其中的各个运算,而是根据算符间的优先关系来确定运算的次序,此外,还应顾及括号规则。
因此,要从中缀表达式直接产生目标代码一般比较麻烦。
相对的,逆波兰式在计算机看来却是比较简单易懂的结构。
因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。
三、实验意义对于实现逆波兰式算法,难度并不大,但为什么要将看似简单的中缀表达式转换为逆波兰式,原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中缀表达式是非常复杂的结构。
相对的,逆波兰式在计算机看来却是比较简单易懂的结构。
因为计算机普遍采用的内存结构是栈式结构,它执行四、系统分析词法分析基本原理:词法分析程序完成的是编译第一阶段的工作。
逆波兰式(后缀表达式)的计算输⼊:后缀表达式(可带浮点数)输出:double型的计算结果代码:#include <stdio.h>#include <stdlib.h>#include <malloc.h>#define ElemType double#define Stack_Init_Size 100#define Increase_Size 10#define MaxBuffer 10typedef struct sqStack{ElemType *top;ElemType *base;int initSize;}sqStack;typedef struct sqStack *LinkStack;//初始化void InitStack( sqStack *s ){s->base = (LinkStack)malloc(Stack_Init_Size * sizeof(ElemType));if(!s->base){printf("存储空间分配失败······\n");return;}s->top = s->base;s->initSize = Stack_Init_Size;}//进栈void Push(sqStack *s,ElemType e){if(s->top - s->base >= s->initSize - 1){s->base = (LinkStack)realloc(s->base,(s->initSize + Increase_Size) * sizeof(ElemType));//第⼀个s->base是增加后的存储空间块的地址,第⼆个是增加空间之前的存储空间块地址,后⾯是增加过的存储空间块的⼤⼩ if(!s->base){printf("增加存储空间失败······\n");return;}s->initSize = Increase_Size + Stack_Init_Size;}*(s->top) = e;(s->top)++;}//出栈void Pop(sqStack *s,ElemType *e){if(s->top == s->base){printf("栈已空,⽆法进⾏出栈操作······\n");return;}s->top--;*e = *s->top;}//求栈的长度int StackLen(sqStack s){return (s.top - s.base);}//逆波兰计算器:输⼊逆波兰式(后缀表达式)输出结果int main(){int i = 0,j,len;double m,n,t;char c;struct sqStack s;char str[MaxBuffer];InitStack(&s);printf("请输⼊您要计算的后缀表达式,按Enter键结束(两个不同的字符之间⽤空格隔开):\n");scanf("%c",&c);while(c != '\n'){while( (c >= '0'&&c <= '9') || c == '.'){str[i] = c;i++;// str[i] = '\0';if(i >= 10){printf("\n输⼊的数字过⼤导致出错\n"); return -1;}scanf("%c",&c);if( c == ' '){t = atof(str);// printf("\nt is %f\n",t);Push(&s,t);i = 0;for(j = 0;j < MaxBuffer;j++){str[j] = '\0';}break;}}switch( c ){case '+':Pop(&s,&m);Pop(&s,&n);Push(&s,n+m);break;case '-':Pop(&s,&m);Pop(&s,&n);Push(&s,n-m);break;case '*':Pop(&s,&m);Pop(&s,&n);Push(&s,n*m);break;case '/':Pop(&s,&m);Pop(&s,&n);if( m == 0){printf("\n除数为0,出错\n");return -1;}else{Push(&s,n/m);break;}}scanf("%c",&c);}Pop(&s,&t);printf("\n最终的计算结果为:%f \n",t);return 0;}。
逆波兰表达式求值一、需求分析1、从键盘中输入一个后缀表达式,该表示包括加减乘除等操作符,以及正整数作为操作数等。
2、用堆栈来实现3、测试数据输入:2 3 * 1 – #输出:2 3 * 1 -- =5二、概要设计抽象数据类型需要一个浮点数栈来存储还没有计算的浮点数或者运算的结果。
ADT Stack数据成员:int size; int top; //分别用于存储栈大小、栈顶位置float *listArray;//存储浮点型数字的数组成员函数:bool push(float it);bool pop(float& it);bool isEmpty(); //判断栈为空bool isOne();//判断栈是否只有一个元素算法的基本思想1. 逐一扫描字符串,用ascii码进行判断,如果该字符是数字,则利用x=x*10+str[i]-48将数据由字符类型转换为浮点型数据;2. 如果字符是‘.’,则将‘.’转化为小数点,并将‘.’后的数据转化为小数部分;3. 遇到空格前是数据的,将x押入栈;4. 如果该字符是’+’,’-’,’*’或’/’,判断栈里的元素是否少于两个个,如果少于两个,报错;如果大于等于两个,就弹出两个数据,并进行相应的计算;程序的流程输入字符串,程序对字符串依次扫描。
扫描一位,处理一位。
扫描完成后,判断栈里是不是只有一个数据,若是,得到正确结果;若不是,则表达式出错。
三、详细设计物理数据类型用浮点数类型的栈存储运算中要用的数据,需要入栈、出栈,故设计如下的浮点类型的栈:class Stack{private:int size;int top;float *listArray;public:Stack(int sz=20);~Stack();bool push(float it);//入栈bool pop(float& it);//出栈bool isEmpty();//判断栈是否为空bool isOne(); //判断栈里是否只有且仅有一个元素};成员函数的函数体Stack::Stack(int sz) //栈构造函数{size=sz;top=0;listArray=new float[size]; }bool Stack::push(float it) {if(top==size)return false;listArray[top++]=it;return true;}bool Stack::pop(float& it) {if(top==0)return false;it=listArray[--top];return true;}bool Stack::isEmpty() //判断站是否为空{if(top==0)return true;return false;}bool Stack::isOne(){if(top==1)return true;return false;}Stack::~Stack(){delete listArray;}算法的具体步骤用switch语句实现1. 逐一扫描字符串,用ascii码进行判断,如果该字符是数字,则利用x=x*10+str[i]-48将数据由字符类型转换为浮点型数据;2. 如果字符是‘.’,则将‘.’转化为小数点,并将‘.’后的数据转化为小数部分;3. 遇到空格前是数据的,将x押入栈;4. 如果该字符是’+’,’-’,’*’或’/’,判断栈里的元素是否少于两个个,如果少于两个,报错;如果大于等于两个,就弹出两个数据,并进行相应的计算;算法的时空分析因为入栈、出栈的时间复杂度均为Θ(1),所以时间的复杂度主要取决于字符串的长度,空间也同样取决于字符串长度。
逆波兰算法-python代码实现1,逆波兰算法简介假定给定⼀个只 包含 加、减、乘、除,和括号的算术表达式,你怎么编写程序计算出其结果?问题是:在表达式中,括号,以及括号的多层嵌套 的使⽤,运算符的优先级不同等因素,使得⼀个算术表达式在计算时,运算顺序往往因表达式的内容⽽定,不具规律性。
这样很难编写出统⼀的计算指令。
使⽤逆波兰算法可以轻松解决这个问题。
他的核⼼思想是将普通的中缀表达式转换为后缀表达式。
什么是中缀表达式?例如a+b,运算符在两个操作数的中间。
这是我们从⼩学开始学习数学就⼀直使⽤的表达式形式。
什么是后缀表达式?例如a b + ,运算符在两个操作数的后⾯。
后缀表达式虽然看起来奇怪,不利于⼈阅读,但利于计算机处理。
转换为后缀表达式的好处是:1、去除原来表达式中的括号,因为括号只指⽰运算顺序,不是实际参与计算的元素。
2、使得运算顺序有规律可寻,计算机能编写出代码完成计算。
2,逆波兰算法原理逆波兰算法的核⼼步骤就2个:1、将中缀表达式转换为后缀表达式,例如输⼊的原始表达式是 3*(5+7) ,转换得到 3 5 7 + *2、根据后缀表达式,按照特定的计算规则得到最终计算结果下⾯详细介绍这个2步的操作。
中缀表达式转换为后缀表达式你需要设定⼀个栈SOP,和⼀个线性表 L 。
SOP⽤于临时存储运算符和左括号分界符( ,L⽤于存储后缀表达式。
遍历原始表达式中的每⼀个表达式元素(1)如果是操作数,则直接追加到 L中。
只有 运算符 或者 分界符( 才可以存放到 栈SOP中(2)如果是分界符Ⅰ 如果是左括号 ( , 则 直接压⼊SOP,等待下⼀个最近的 右括号 与之配对。
Ⅱ 如果是右括号),则说明有⼀对括号已经配对(在表达式输⼊⽆误的情况下)。
不将它压栈,丢弃它,然后从SOP中出栈,得到元素e,将e依次追加到L⾥。
⼀直循环,直到出栈元素e 是 左括号 ( ,同样丢弃他。
(3)如果是运算符(⽤op1表⽰)Ⅰ如果SOP栈顶元素(⽤op2表⽰) 不是运算符,则⼆者没有可⽐性,则直接将此运算符op1压栈。
C语⾔实现逆波兰式实例复制代码代码如下:#include<stdio.h>#include<string.h>typedef struct{char s[20][20];int top;}SQ;void copystr(char *a,char *b){int i=0;do{b[i]=a[i];i++;}while(a[i]!='\0');b[i]='\0';}void voidSQ(SQ *s){s->top=-1;}int ifempty(SQ *s){return(s->top==-1);}void push(SQ *S,char *c){if(S->top==19)printf("over flow\n");else{S->top++;copystr(c,S->s[S->top]);}}char *pop(SQ *S){if(ifempty(S)){printf("over flow!\n");return(NULL);}elsereturn(S->s[S->top--]);}int judge(char *c){if(c[1]=='\0')switch(c[0]){case '+':return(3);case '-':return(3);case '*':return(2);case '/':return(2);default:return(1);}elsereturn(1);}void write(char *a,char *b,char *c){strcat(a,c);strcat(a,b);}int seek(char *c,int start){int signal=1;for(start=start++;c[start]!='\0'&&signal!=0;start++) {if(c[start]==')')signal--;else if(c[start]=='(')signal++;}if(signal==0)return(start-1);else{printf("输⼊⽆效式⼦\n");return(-1);}}void FB(SQ *A,SQ *B){for(;!ifempty(A);){push(B,A->s[A->top]);pop(A);}}char *rewrite(char *A){SQ front;SQ back;int i,j,k,flag=0;char *result;char mid[20];voidSQ(&front);voidSQ(&back);for(i=0;A[i]!='\0';){if(A[i]=='('){j=seek(A,i);for(k=i+1;k<j;k++){mid[k-i-1]=A[k];}mid[j-i-1]='\0';copystr(rewrite(mid),mid);push(&back,mid);i=j+1;}else if(A[i]!='(')mid[0]=A[i];mid[1]='\0';push(&back,mid);i++;}}FB(&back,&front);for(;front.top>=2;){flag=0;for(i=0;i<=front.top;i++){if(judge(front.s[i])==2){flag=1;break;}}if(flag==1){for(;front.top>=2;){if(judge(front.s[front.top])==1&&judge(front.s[front.top-1])==2&&judge(front.s[front.top-2])==1) {write(front.s[front.top],front.s[front.top-1],front.s[front.top-2]);push(&back,front.s[front.top]);pop(&front);pop(&front);pop(&front);}else{push(&back,front.s[front.top]);pop(&front);}}FB(&front,&back);FB(&back,&front);}else{for(;front.top>=2;){if(judge(front.s[front.top])==1&&judge(front.s[front.top-1])==3&&judge(front.s[front.top-2])==1) {write(front.s[front.top],front.s[front.top-1],front.s[front.top-2]);push(&back,front.s[front.top]);pop(&front);pop(&front);pop(&front);}else{push(&back,front.s[front.top]);pop(&front);}}FB(&front,&back);FB(&back,&front);}result=front.s[front.top];return(result);}typedef struct{char c[20];int top;}sq; int execute(char a,char b,char c) {switch(a){case('+'):return((c-48)+(b-48)); case('-'):return((c-48)-(b-48));case('*'):return((c-48)*(b-48));case('/'):return((c-48)/(b-48));}}void voidsq(sq *s){s->top=-1;}int ifsqempty(sq *s){return(s->top==-1);}void pushsq(sq *s,char x){if(s->top==19)printf("over flow!\n");else{s->top=s->top+1;s->c[s->top]=x;}}void popsq(sq *s){if(ifsqempty(s))printf("over flow!\n");elses->top--;}int just(char c){switch(c){case ('+'):return(0);case ('-'):return(0);case ('*'):return(0);case ('/'):return(0);default:return(1);}}void restread(sq *a,sq *b){for(;!ifsqempty(a);){pushsq(b,a->c[a->top]);popsq(a);}}int calculate(char *c){sq rest,read;int i,re;voidsq(&rest);voidsq(&read);for(i=0;c[i]!='\0';i++)pushsq(&read,c[i]);for(;read.top>=2;){for(;read.top>=2;){if(just(read.c[read.top])==0&&just(read.c[read.top-1])==1&&just(read.c[read.top-2]) ==1) {re=execute(read.c[read.top],read.c[read.top-1],read.c[read.top-2]);pushsq(&rest,re+48);popsq(&read);popsq(&read);popsq(&read);}else{pushsq(&rest,read.c[read.top]);popsq(&read);}}restread(&read,&rest);restread(&rest,&read);}return(read.c[0]-48);}void main(){char re[20];char a[20];printf("请输⼊算式:\n");scanf("%s",a);copystr(rewrite(a),re);printf("逆波兰式:\n%s\n",re);printf("求值结果:\n%d\n",calculate(re));}。
C语言课程设计之逆波兰表达式//逆波兰表达式(后缀表达式)reverse polish notation//程序实现的功能是将中缀表达式转变为后缀表达式,再求出其值//主要运用的知识点有:isdigit函数,pow函数,system("cls")函数,堆栈,格式的强制转换#include<stdio.h>#include<ctype.h>#include<stdlib.h>#include<math.h>void shift( char notation[]); //中缀表达式转换为后缀表达式的转换函数float calculate(float a[][2],int k); //计算后缀表达式int judge(char notation[]); //判断输入的中缀表达式是否符合要求int grade(char a); //返回运算符的等级void display(float a[][2],int k); //在屏幕上显示后缀表达式//主函数void main(){char notation [100];char choice;do{printf("请输入正确的中缀表达式:\n");printf("例如:2*3+4/3-(2+1)\n");scanf("%s",¬ation);if(judge(notation)){shift(notation);}elseprintf("你的表达式有错误,请仔细检查!\n");fflush(stdin);printf("\n你是否需要继续计算(是输入Y/y,否输入其他任意键)\n");scanf("%c",&choice);getchar();system("cls");}while(choice=='Y'||choice=='y');printf("\n程序结束,谢谢使用!\n");}//判定函数int judge(char notation[]){int i,m,num=1,p1=0,p2=0;for(i=0;notation[i]!='\0';i++) //排除表达式外的字符{if(notation[i]!='('&¬ation[i]!=')'&¬ation[i]!='+'&¬ation[i]!='-'&¬ation[i]!='*'&¬ation[i]!='/'&&!isdigit(notation[i])&¬ation[i]!='.') {num=0;return num;}}if(notation[0]=='*'||notation[0]=='/'||notation[0]==')'||notation[0]=='.') //排除第一个字符为*,/,),.{num=0;return num;}for(i=0;notation[i]!='\0';i++) //排除'+','-','*','/','.'之间的连续出现以及'+','-','*','/','.'后面直接加')'{if(notation[i]!='('&¬ation[i]!=')'&&!isdigit(notation[i])){if(notation[i+1]!='('&&!isdigit(notation[i+1])){num=0;return num;}}if(notation[i]=='('&&(notation[i+1]==')'||notation[i+1]=='.'||notation[i+1]=='*'||notation[i+ 1]=='/')){ //排除'('和')','.','*','/'一起连用num=0;return num;}if(notation[i]==')'&&(notation[i+1]=='('||notation[i+1]=='.'))//排除')'和'(','.'一起连用{num=0;return num;}}for(i=0;notation[i]!='\0';i++) //小数位不得超过4位{if(notation[i]=='.'&¬ation[i+1]!='\0'&¬ation[i+2]!='\0'&¬ation[i+3]!='\0'&¬ation[i+4]!='\0'&¬ation[i+5]!='\0'){if(isdigit(notation[i+1])&&isdigit(notation[i+2])&&isdigit(notation[i+3])&&isdigit(notation[i+ 4])&&isdigit(notation[i+5])){num=0;return num;}}}for(i=0;notation[i]!='\0';i++) //排除一个小数中有两个小数点的情况{if(notation[i]=='.'){i++;while(isdigit(notation[i])){i++;}if(notation[i]=='.'){num=0;return 0;}}}for(i=0;notation[i]!='\0';i++) //排除')'后面不可以直接跟数字以及'('前面不可以加数字{if(notation[i]==')'&&isdigit(notation[i+1])){num=0;return num;}if(isdigit(notation[i])&¬ation[i+1]=='(' ){num=0;return num;}}for(i=0;notation[i]!='\0';i++) //约束数字的位数一共最多为七位{if(isdigit(notation[i])){m=0; //用来计数,数字的位数为7while(isdigit(notation[i])||notation[i]=='.'){i++;m++;if(notation[i]=='.'){m--;}}if(m>7){num=0;return num;}}}for(i=0;notation[i]!='\0';i++) //'('与')'需要配对存在{if(notation[i]=='(')p1++;if(notation[i]==')')p2++;if(p1!=p2){num=0;return num;}}return num;}//转换函数void shift( char notation[]){char s1[100];s1[0]='#';float s2[100][2]; //第一维放后缀表达式的元素,第二维表示小数点的位数以及是否是运算符int i=0,j=1,k=0,t=0;float sum,num1=0,num2=0; //num1为存储整数位num2为存储小数位while(notation[i]!='\0'){if(i==0&¬ation[i]=='+') //第一位为正号的情况{if(isdigit(notation[++i])){num1=0; //整数部分while(isdigit(notation[i])){num1=num1*10+(notation[i]-'0'); //notation[i]-'0'可以将字符转换为整数0~9i++;}num2=0; //小数部分t=0;if(notation[i]=='.'){i++;while(isdigit(notation[i])){num2=float (num2+pow(0.1,++t)*(notation[i]-'0'));i++;}}s2[k++][0]=float(num1+num2);s2[k-1][1]=float(t);}}if(i==0&¬ation[i]=='-') //第一位为负号的情况,代码与正号类似{if(isdigit(notation[++i])){num1=0;while(isdigit(notation[i])){num1=(-1)*num1*10+(-1)*(notation[i]-'0');i++;}num2=0;t=0;if(notation[i]=='.'){i++;while(isdigit(notation[i])){num2=float(num2+(-1)*pow(0.1,++t)*(notation[i]-'0'));i++;}}s2[k++][0]=float(num1+num2);s2[k-1][1]=float(t);}}if(isdigit(notation[i])) //当前字符为数字的情况与为正号的情况一样{num1=0;while(isdigit(notation[i])){num1=num1*10+(notation[i]-'0');i++;}num2=0;t=0;if(notation[i]=='.'){i++;while(isdigit(notation[i])){num2=float(num2+pow(0.1,++t)*(notation[i]-'0'));i++;}}s2[k++][0]=float(num1+num2);s2[k-1][1]=float(t);}if(notation[i]=='+'||notation[i]=='-'||notation[i]=='*'||notation[i]=='/'){ //当前的字符为操作符时,如果s1的站定为'('则将字符直接送入s1if(s1[j-1]=='('){s1[j++]=notation[i++];}}if(notation[i]=='+'||notation[i]=='-'||notation[i]=='*'||notation[i]=='/'){ //当前字符为操作符时的普通的情况if(grade(notation[i])>grade(s1[j-1])){s1[j++]=notation[i++];}else{s2[k++][0]=s1[--j];s2[k-1][1]=-1;s1[j++]=notation[i++];}}if(notation[i]=='(') //当前字符为'('的情况{s1[j++]=notation[i++];if(notation[i]=='+') //'('后跟正号的情况{if(isdigit(notation[++i])){num1=0;while(isdigit(notation[i])){num1=num1*10+(notation[i]-'0');i++;}num2=0;t=0;if(notation[i]=='.'){i++;while(isdigit(notation[i])){num2=float(num2+pow(0.1,++t)*(notation[i]-'0'));i++;}}s2[k++][0]=float(num1+num2);s2[k-1][1]=float(t);}}if(notation[i]=='-') //'('后跟负号的情况{if(isdigit(notation[++i])){num1=0;while(isdigit(notation[i])){num1=float((-1)*num1*10+(-1)*(notation[i]-'0'));i++;}num2=0;t=0;if(notation[i]=='.'){i++;while(isdigit(notation[i])){num2=float(num2+(-1)*pow(0.1,++t)*(notation[i]-'0'));i++;}}s2[k++][0]=float(num1+num2);s2[k-1][1]=float(t);}}}if(notation[i]==')') //当前字符为')'的情况{while(s1[--j]!='('){s2[k++][0]=s1[j];s2[k-1][1]=-1;}i++;}}while(j>0&&s1[--j]!='#') //依次将s1中的除了'#'外的所有操作符出栈,相当于最后的扫尾工作{s2[k++][0]=s1[j];s2[k-1][1]=-1;}printf("\n后缀表达式(逆波兰表达式):\n");display(s2,k-1);printf("\n表达式的值为:\n");sum=calculate(s2,k-1);printf("%7.4f",sum);}//计算函数float calculate(float a[][2],int k){int i,t=0,j=k;float b[100][2],c[100];for(i=k;i>=0;i--){b[i][0]=a[k-i][0];b[i][1]=a[k-i][1];}i=k;while(j>=0){if(b[i][1]!=-1){c[t]=float (b[i][0]);j--;i--;t++;}if(b[i][1]==-1) //每当遇到一个运算符则将栈最上面的两个数出栈进行运算,然后再入栈{if(int(b[i][0])=='+'){c[t-2]=float (c[t-2]+c[t-1]);}if(int(b[i][0])=='-'){c[t-2]=float (c[t-2]-c[t-1]);}if(int(b[i][0])=='*'){c[t-2]=float (c[t-2]*c[t-1]);}if(int(b[i][0])=='/'){c[t-2]= float (c[t-2]/c[t-1]);}j--;i--;t--;}}return c[0]; //运算到最后,栈中的元素即为结果}//等级函数int grade(char a) //按照运算符的优先级{if(a=='#')return 0;if(a=='(')return 1;if(a=='-'||a=='+')return 2;if(a=='*'||a=='/')return 3;if(a==')')return 4;elsereturn 5;}//显示函数void display(float a[][2],int k){int i;for(i=0;i<=k;i++){if(a[i][1]==0)printf(" %d",int(a[i][0]));if(a[i][1]==1)printf(" %7.1f",a[i][0]);if(a[i][1]==2)printf(" %7.2f",a[i][0]);if(a[i][1]==3)printf(" %7.3f",a[i][0]);if(a[i][1]==4)printf(" %7.4f",a[i][0]);if(a[i][1]==-1)printf(" %c",int (a[i][0]));}}算法实现一个表达式E的后缀形式可以如下定义:(1)如果E是一个变量或常量,则E的后缀式是E本身。
一.实验目的
1.深入理解算符优先分析法
2.掌握FirstVt和LastVt集合的求法有算符优先关系表的求法
3.掌握利用算符优先分析法完成中缀表达式到逆波兰式的转化
二.实验内容及要求
将非后缀式用来表示的算术表达式转换为用逆波兰式来表示的算术表达式,并计算用逆波兰式来表示的算术表达式的值。
程序输入/输出示例:
输出的格式如下:
(1)
(2)输入一以#结束的中缀表达式(包括+—*/()数字#)
(3)
(4)
备注:(1)在生成的逆波兰式中如果两个数相连则用&分隔,如28和68,中间用&分隔;
注意:1.表达式中允许使用运算符(+-*/)、分割符(括号)、数字,结束符#;
2.如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好);
3.对学有余力的同学,测试用的表达式事先放在文本文件中,一行存放一个表达式,同时以分号分割。
同时将预期的输出结果写在另一个文本文件中,以便和输出进行对照;
三.实验过程
1、逆波兰式定义
将运算对象写在前面,而把运算符号写在后面。
用这种表示法表示的表达式也称做后缀式。
逆波兰式的特点在于运算对象顺序不变,运算符号位置反映运算顺序。
采用逆波兰式可以很好的表示简单算术表达式,其优点在于易于计算机处理表达式。
2、产生逆波兰式的前提
中缀算术表达式
3、逆波兰式生成的实验设计思想及算法
(1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
(2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。
(3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。
(4)如果不是数字,该字符则是运算符,此时需比较优先关系。
做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。
如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。
倘若不是的话,则将此运算符栈顶的运算符从栈中弹出,将该字符入栈。
(5)重复上述操作(1)-(2)直至扫描完整个简单算术表达式,确定所有字符都得到正确处
理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。
3、逆波兰式计算的实验设计思想及算法
(1)构造一个栈,存放运算对象。
(2)读入一个用逆波兰式表示的简单算术表达式。
(3)自左至右扫描该简单算术表达式并判断该字符,如果该字符是运算对象,则将该字符入栈。
若是运算符,如果此运算符是二目运算符,则将对栈顶部的两个运算对象进行该运算,将运算结果入栈,并且将执行该运算的两个运算对象从栈顶弹出。
如果该字符是一目运算符,则对栈顶部的元素实施该运算,将该栈顶部的元素弹出,将运算结果入栈。
(4)重复上述操作直至扫描完整个简单算术表达式的逆波兰式,确定所有字符都得到正确处理,我们便可以求出该简单算术表达式的值。
4.实验的源程序代码如下:
#include<stdio.h>
#include<math.h>
#define max 100
char ex[max]; /*存储后缀表达式*/
void trans(){ /*将算术表达式转化为后缀表达式*/ char str[max]; /*存储原算术表达式*/
char stack[max]; /*作为栈使用*/
char ch;
int sum,i,j,t,top=0;
printf("*****************************************\n"); printf("*输入一个求值的表达式,以#结束。
*\n"); printf("******************************************\n"); printf("算数表达式:");
i=0; /*获取用户输入的表达式*/ do{
i++;
scanf("%c",&str[i]);
}while(str[i]!='#' && i!=max);
sum=i;
t=1;i=1;
ch=str[i];i++;
while(ch!='#'){
switch(ch){
case '(': /*判定为左括号*/
top++;stack[top]=ch;
break;
case ')': /*判定为右括号*/
while(stack[top]!='('){
ex[t]=stack[top];top--;t++;
}
top--;
break;
case '+': /*判定为加减号*/
case '-':
while(top!=0&&stack[top]!='('){
ex[t]=stack[top];top--;t++;
}
top++;stack[top]=ch;
break;
case '*': /*判定为乘除号*/
case '/':
while(stack[top]=='*'||stack[top]=='/'){
ex[t]=stack[top];top--;t++;
}
top++;stack[top]=ch;
break;
case ' ':break;
default:while(ch>='0'&&ch<='9'){ /*判定为数字*/
ex[t]=ch;t++;
ch=str[i];i++;
}
i--;
ex[t]='#';t++;
}
ch=str[i];i++;
}
while(top!=0){
ex[t]=stack[top];t++;top--;
}
ex[t]='#';
printf("\n\t原来表达式:");
for(j=1;j<sum;j++)
printf("%c",str[j]);
printf("\n\t后缀表达式:",ex);
for(j=1;j<t;j++)
printf("%c",ex[j]);
}
void compvalue(){ /*计算后缀表达式的值*/ float stack[max],d; /*作为栈使用*/
char ch;
int t=1,top=0; /*t为ex下标,top为stack下标*/ ch=ex[t];t++;
while(ch!='#'){
switch(ch){
case '+':
stack[top-1]=stack[top-1]+stack[top];
top--;
break;
case '-':
stack[top-1]=stack[top-1]-stack[top];
top--;
break;
case '*':
stack[top-1]=stack[top-1]*stack[top];
top--;
break;
case '/':
if(stack[top]!=0)
stack[top-1]=stack[top-1]/stack[top];
else{
printf("\n\t除零错误!\n");
exit(0); /*异常退出*/
}
top--;
break;
default:
d=0;
while(ch>='0'&&ch<='9'){
d=10*d+ch-'0'; /*将数字字符转化为对应的数值*/
ch=ex[t];t++;
}
top++;
stack[top]=d;
}
ch=ex[t];t++;
}
printf("\n\t计算结果:%g\n",stack[top]);
}
main(){
trans();
compvalue();
}
5.程序的运行结果如下:
四.实验总结(心得)
通过本实验的学习,主要掌握了逆波兰式算法的含义,结合书本知识,让我更加清楚此算法实现的主要过程,以及使用该算法的好处,通过对程序实现的分析,对于逆波兰式算法的细节了解的更加清楚,从中学到了很多的东西。