求前缀表达式的值

求前缀表达式的值7 1 nbsp 求前缀表达式的值 20 nbsp 分 算术表达式有前缀表示法 中缀表示法和后缀表示法等形式 前缀表达式指二元运算符位于两个运算数之前 例如 2 3 7 4 8 4 的前缀表达式是 2 3 74 84 请设计程序计算前缀表达式的结果值 输入格式 输入在一行内给出不超过 30 个字符的前缀表达式 只包含 以及运算数 不同对象 运算数 运算符号 之间以空格分隔 输出格式 输

7-1 求前缀表达式的值(20 分)

算术表达式有前缀表示法、中缀表示法和后缀表示法等形式。前缀表达式指二元运算符位于两个运算数之前,例如2+3*(7-4)+8/4的前缀表达式是:+ + 2 * 3 - 7 4 / 8 4。请设计程序计算前缀表达式的结果值。

输入格式:

输入在一行内给出不超过30个字符的前缀表达式,只包含+-*\以及运算数,不同对象(运算数、运算符号)之间以空格分隔。

输出格式:

输出前缀表达式的运算结果,保留小数点后1位,或错误信息ERROR

输入样例:

+ + 2 * 3 - 7 4 / 8 4 

输出样例:

13.0

法一

提交结果

提交时间 状态 分数 题目 编译器 耗时 用户
2018/5/24 22:32:52 答案正确 20 7-1 C++ (g++) 3 ms 17GJ54
测试点 提示 结果 耗时 内存
0 sample 4种运算 答案正确 2 ms 236KB
1 运算数超过1位整数且有负号 答案正确 2 ms 240KB
2 非正常退出 答案正确 2 ms 240KB
3 只有1个数字,前有+号 答案正确 3 ms 240KB


解题思路

首先了解一下前缀表达式



前缀表达式就是不含括号的算术表达式,而且它是将运算符写在前面,

操作数写在后面的表达式,也称为“波兰式”。例如,- 1 + 2 3,它等价于1-(2+3);




前缀表达式如何求值



对于一个前缀表达式的求值而言,首先要从右至左扫描表达式,从右边第一个字符开始判断,如果当前字符是数字则一直到数字串的末尾再记录下来,如果是运算符,则将右边离得最近的两个“数字串”作相应的运算,以此作为一个新的“数字串”并记录下来。一直扫描到表达式的最左端时,最后运算的值也就是表达式的值。
例如,前缀表达式“- 1 + 2 3“的求值,扫描到3时,记录下这个数字串,扫描到2时,记录下这个数字串,当扫描到+时,将+右移做相邻两数字的运算符,记为2+3,结果为5,记录下这个新数字串,并继续向左扫描,扫描到1时,记录下这个数字串,扫描到-时,将-右移做相邻两数字串的运算符,记为1-5,结果为-4,

所以表达式的值为-4。




前缀表达式有什么用处



前缀表达式是一种十分有用的表达式,它将中缀表达式转换为可以依靠简单的操作就能得到运算结果的表达式。例如,(a+b)*(c+d)转换为 *,+,a,b,+,c,d。它的优势在于只用两种简单的操作,入栈和出栈就可以解决任何中缀表达式的运算。其运算方式为:如果当前字符(或字符串)为数字或变量,则压入栈内;如果是运算符,则将栈顶两个元素弹出栈外并作相应运算,再将结果压入栈内。当前缀表达式扫描结束时,栈里的就是中缀表达式运算的最终结果。(https://wenku.baidu.com/view/5664602d647d27284b7351f7.html)



如何解这道题目



既然知道了如何计算前缀表达式,但是麻烦的是如何处理数据?
看了网上很多人的代码,觉得写的都不太简练,这里想到了一种比较简洁的处理方式,使用c++的string和c语言的 atof()函数 。



#include       #include         #include           #include             #include               #include                 using namespace std; stack                   q; int main(){ string a[100]; bool error = 0; int n = 0; while(cin>>a[n++]){}///读到文件结尾自动结束 n -= 1;///注意这里n是个数,需要减一,可自己尝试确认 for(int i = n-1; i>=0; i--){ ///如果是符号 if(a[i].length() == 1 && (a[i][0]== '+' || a[i][0]=='-' || a[i][0]=='*' || a[i][0]=='/')){ if(q.size()<2) {error = 1; break;} double aa = q.top(); q.pop(); double bb = q.top(); q.pop(); if(a[i][0]== '+') q.push(aa+bb); else if(a[i][0]== '-') q.push(aa-bb); else if(a[i][0]== '*') q.push((aa*bb)); else if(a[i][0]== '/') { if(bb==0){error = 1; break;} q.push(aa/bb); } } else{ /// c_str() 函数是转化为字符数组 ///atof() 是c语言中将字符数组转化为浮点型数据函数 double x = atof(a[i].c_str()); q.push(x); } } if(q.size() != 1) error = 1; if(error) printf("ERROR"); else printf("%.1f",q.top()); return 0; }                                          

法二

提交时间 状态 分数 题目 编译器 耗时 用户
2018/5/24 22:38:37 答案正确 20 7-1 C (gcc) 2 ms 17GJ54
测试点 提示 结果 耗时 内存
0 sample 4种运算 答案正确 2 ms 128KB
1 运算数超过1位整数且有负号 答案正确 2 ms 128KB
2 非正常退出 答案正确 2 ms 128KB
3 只有1个数字,前有+号 答案正确 2 ms 128KB

/* 求前缀表达式的值 */ #include 
   
     #include 
    
      #include 
     
       #include 
      
        #define N 30 #define TRUE 1 #define FALSE 0 typedef struct { double data[N]; int top; } Stack; /* 入栈 */ void Push( Stack *ptrs, double item ) { if( ptrs->top == N - 1 ){ printf( "Stack is full.\n" ); return; } else { ptrs->data[ ++( ptrs->top ) ] = item; return; } } /* 岀栈 */ double Pop( Stack * ptrs ) { if( ptrs->top == -1 ) { printf( "Stack is empty.\n" ); return; } else return ptrs->data[ ( ptrs->top )-- ]; } /* 判断是否操作符 */ int IsOperator( char ch ) { if( ch == '+' || ch == '-' || ch == '*' || ch == '/' ) return TRUE; else return FALSE; } /* 计算 */ double Calculate( double a, double b,char ch ) { switch( ch ) { case '+' : return a + b; break; case '-' : return a - b; break; case '*' : return a * b; break; case '/' : return a / b; } } int main() { char expr[N]; gets( expr ); int len = strlen( expr ); Stack ss; ss.top = -1; double cc = 1; double tem_sum = 0; double operand_a; double operand_b; double result; int error = 0; // 记录除数为0的错误情况 int i; for( i = len - 1; i >= 0; -- i ) { if( expr[i] >= '0' && expr[i] <= '9' ) { tem_sum += ( expr[i] - '0' ) * cc; cc *= 10; if( expr[i-1] == '+' ) { //printf( "%d\n", tem_sum ); Push( &ss, tem_sum ); tem_sum = 0; cc = 1; i -= 2; // 跳过下一个正号和空格 continue; } else if( expr[i-1] == '-' ) { tem_sum = -tem_sum; //printf( "%d\n", tem_sum ); Push( &ss, tem_sum ); tem_sum = 0; i -= 2; // 跳过下一个负号和空格 continue; } else if( expr[i-1] == ' ' ) { // 一个数字处理完了 //printf( "%d\n", tem_sum ); Push( &ss, tem_sum ); tem_sum = 0; cc = 1; i --; continue; } } else if( expr[i] == '.' ) { tem_sum /= cc * 1.0; cc = 1; } else if( IsOperator( expr[i] ) ) { operand_a = Pop( &ss ); operand_b = Pop( &ss ); if( expr[i] == '/' && operand_b == 0 ) { error = 1; break; } else { result = Calculate( operand_a, operand_b, expr[i] ); //printf( "result:%.1lf\n", result ); Push( &ss, result ); i--; // 跳过下一个空格 } } } if( error != 1 ) printf( "%.1lf\n", Pop( &ss ) ); else printf( "ERROR\n" ); return 0; } 
       
      
     
   

法三

提交时间 状态 分数 题目 编译器 耗时 用户
2018/5/24 23:02:00 答案正确 20 7-1 C++ (g++) 2 ms 17GJ54
测试点 提示 结果 耗时 内存
0 sample 4种运算 答案正确 2 ms 128KB
1 运算数超过1位整数且有负号 答案正确 2 ms 128KB
2 非正常退出 答案正确 2 ms 128KB
3 只有1个数字,前有+号 答案正确 2 ms 128KB

(用c++编译器才行)

求前缀表达式的算法不难,这题合适的数据结构显然是栈。这题稍复杂的地方就是对输入的处理以及栈的基本操作。

首先,输入的每一项数据存储在栈的每一个节点中这是确定的。但是所输入的数据的类型却不一致。栈的节点struct Node里设计了四个成员变量。
struct Node{

char opr;
float num;
int type; //用来标记该节点是个操作符还是操作数
struct Node* Next;
};

我们最终要把所输入的操作符以char形式存储,所输入的操作数以float形式存储。

这里,先统一以字符串形式通过scanf(“%s”)输入,用空格隔开。然后再判断该字符串是表示操作数还是操作符,并通过设计两个相应函数把字符串转化为字符形式的操作符 以及 把字符串转化为float操作数。

每输入一项数据后,都要对栈进行相应操作。
如果输入的是操作符则push。
如果当前输入的是操作数则分两种情况。
0. 此时是空栈,直接push进栈。
1. 此时栈顶是操作符,则直接push进栈。
2. 此时栈顶也是操作数,即连续出现了两个操作数,那么弹出栈顶的操作数(而后栈顶指针已变),再弹出栈顶的操作符。通过这个操作数、操作符以及当前输入的操作数计算得出一个新的操作数。此时再把这个新的操作数压入栈中,进栈操作同样递归地进行栈顶是符号还是数字的判断,直至可直接push进栈。
最后栈顶操作数即为前缀表达式的结果。

除此之外,需注意的细节有:
1. 注意ERROR情况的出现,进行除法操作前判断作为除数的操作数是否为零。
2. 成员变量Next未显式赋值的时候并没有默认为NULL

















































#include 
   
     #include 
    
      #include 
     
       #include 
      
        typedef struct Node* Stack; typedef struct Node* Position; struct Node{ char opr; float num; int type; //0表示操作符 1表示数字 struct Node* Next; }; void Push(char c, Stack S) { Position P; Position Temp; Temp = (Position)malloc(sizeof(struct Node)); Temp->opr = c; Temp->type = 0; Temp->Next = NULL; Temp->Next = S->Next; S->Next = Temp; } int IsEmpty(Stack S) { return S->Next == NULL; } float PopNum(Stack S) { Position Temp; float num; Temp = S->Next; num = Temp->num; S->Next = Temp->Next; free(Temp); return num; } char PopOpr(Stack S) { Position Temp; char opr; Temp = S->Next; opr = Temp->opr; if (Temp->Next != NULL) S->Next = Temp->Next; else S->Next = NULL; free(Temp); return opr; } void Push(float n, Stack S) { Position P; Position Temp; Temp = (Position)malloc(sizeof(struct Node)); Temp->num = n; Temp->type = 1; Temp->Next = NULL; if (S->Next == NULL) //如果此前一个是操作符或栈空,那么直接压入 { Temp->Next = S->Next; S->Next = Temp; } else if (S->Next->type == 0) { Temp->Next = S->Next; S->Next = Temp; } else{ //如果此前一个是数字 //char c = S->Next->Next->opr; float m = PopNum(S); char c = PopOpr(S); if (c == '+') Push(n + m, S); else if (c == '-') Push(m - n, S); else if (c == '*') Push(m * n, S); else{ if (n == 0) { printf("ERROR"); exit(0); } else Push(m / n, S); } } } int IsOpr(char in[]) { int length = strlen(in); if (length==1 && (in[0] == '+' || in[0] == '-' || in[0] == '*' || in[0] == '/')) return 1; else return 0; } float StrToNum(char s[]) { int length = strlen(s); float result = 0; int flag = 1; int right = 0; int j = 0; for (int i = 0; i < length; i++) { if (s[i] == '.'){ right = 1; continue; } if (i == 0 && s[i] == '+') flag = 1; else if (i == 0 && s[i] == '-') flag = -1; else if (!right) { result = 10 * result + s[i] - '0'; } else{ j++; result = result + (s[i] - '0') / (pow(10.0, j)); } } return result*flag; } void PrintStack(Stack S) { Position P; P = S->Next; while (P != NULL) { if (P->type == 0) { printf("%c", P->opr); } else printf("%f", P->num); P = P->Next; } printf("\n"); } int main() { char in[30]; char buff; float result = 0; Stack S; S = (Stack)malloc(sizeof(struct Node)); S->Next = NULL; scanf("%s", in); buff = getchar(); if (!IsOpr(in)) //如果输入的第一项就是数字,那这个数字就是结果 { float r = StrToNum(in); printf("%.1f", r); return 0; } //如果输入的第一项是操作符 Push(in[0], S); while (buff != '\n') { scanf("%s", in); if (IsOpr(in)) //如果输入的是操作符,就直接入栈 { Push(in[0], S); } else //如果输入的数字 { Push(StrToNum(in), S); } buff = getchar(); } //PrintStack(S); result = S->Next->num; printf("%.1f", result); return 0; } 
       
      
     
   

法四

提交结果

提交时间 状态 分数 题目 编译器 耗时 用户
2018/5/24 23:21:54 答案正确 20 7-1 C (gcc) 2 ms 17GJ54
测试点 提示 结果 耗时 内存
0 sample 4种运算 答案正确 1 ms 128KB
1 运算数超过1位整数且有负号 答案正确 1 ms 128KB
2 非正常退出 答案正确 2 ms 128KB
3 只有1个数字,前有+号 答案正确 2 ms 128KB

思路  
关键就是读取数据的部分,因为+ -(加减) 会被%d(当做正负)给读走,导致错误。所以不能直接使用if(!scanf(%d))scanf(%c).

#include 
     
       #include 
      
        #include 
       
         typedef struct node *Node; struct node { float num; char operators; int flag;//0-int,1-char Node Next; }; Node ReadData(void); Node Function(Node); float Coculate(char,float,float); int main() { struct node head; Node temp=&head; do { temp->Next=ReadData(); temp=temp->Next; } while(getchar()!='\n'); temp->Next=NULL; temp=&head; while(temp->Next) { if(temp->Next&&temp->Next->Next&&temp->Next->Next->Next) { if(temp->Next->flag==1&&temp->Next->Next->flag==0&&temp->Next->Next->Next->flag==0) { float sum=Coculate(temp->Next->operators,temp->Next->Next->num,temp->Next->Next->Next->num); if(sum==99999)break; temp->Next->Next->Next->num=sum; temp->Next=temp->Next->Next->Next; // printf("[%.1f]",temp->Next->num); temp=&head; } else temp=temp->Next; } else break; } if(head.Next->Next) { printf("ERROR"); } else printf("%.1f",head.Next->num); return 0; } float Coculate(char c,float a,float b) { float sum; switch(c) { case '+': sum=a+b; break; case '-': sum=a-b; break; case '*': sum=a*b; break; case '/': if(!b)return 99999; sum=a/b; break; default : sum=99999; break; } return sum; } Node ReadData(void) { char a[31]; Node temp=(Node)malloc(sizeof(struct node)); scanf("%s",a); int len=strlen(a); if(len==1) { switch(a[0]) { case '+': case '-': case '*': case '/': temp->operators=a[0]; temp->flag=1; break; default: temp->num=a[0]-'0'; temp->flag=0; break; } } else { temp->num=0; temp->flag=0; if(a[0]>='0'&&a[0]<='9') { int i=0; for(; i 
        
          num*=10; temp->num+=a[i]-'0'; } float xiaoshu=1; for(++i; i 
         
           num+=xiaoshu*(a[i]-'0'); } } else { int i=1; for(; i 
          
            num*=10; temp->num+=a[i]-'0'; } float xiaoshu=1; for(++i; i 
           
             num+=xiaoshu*(a[i]-'0'); } if(a[0]=='-')temp->num*=-1; } } return temp; } 
            
           
          
         
        
       
     

编译器输出:a.c: In function ‘ReadData’:
a.c:69:5: warning: ignoring return value of ‘scanf’, declared with attribute warn_unused_result [-Wunused-result]
     scanf(“%s”,a);

     ^~~~~~~~~~~~~

法五

提交结果

提交时间 状态 分数 题目 编译器 耗时 用户
2018/5/24 23:33:38 答案正确 20 7-1 C++ (g++) 3 ms 17GJ54
测试点 提示 结果 耗时 内存
0 sample 4种运算 答案正确 3 ms 236KB
1 运算数超过1位整数且有负号 答案正确 3 ms 240KB
2 非正常退出 答案正确 3 ms 240KB
3 只有1个数字,前有+号 答案正确 3 ms 240KB

计算前缀表达式的过程和计算后缀表达式式的过程相反,它是从后向前扫描的。

#include 
         
           #include 
          
            #include 
           
             #include 
            
              using namespace std; float oper(float f1,float f2, char op) { if(op=='+') return f1+f2; else if(op=='-') return f1-f2; else if(op=='*') return f1*f2*1.0; else if(op=='/') return f1*1.0/f2; } int main() { stack 
             
               s; string prefixExp; getline(cin,prefixExp); int strLen=prefixExp.length(); int temp,i,j; int t1,t2; for(i=strLen-1;i>=0;i--) { string digitStr=""; if(prefixExp[i]=='+' || prefixExp[i]=='-' || prefixExp[i]=='*' || prefixExp[i]=='/') //运算符 { t1=s.top(); s.pop(); t2=s.top(); s.pop(); if(t2==0 && prefixExp[i]=='/') { printf("ERROR\n"); return 0; } s.push(oper(t1,t2,prefixExp[i])); // printf("%f\n",s.top()); i--;//下一位肯定是空格 } else //运算数 { while(i>=0 && prefixExp[i]!=' ') //不要漏掉i>=0条件 { digitStr=prefixExp[i]+digitStr; i--; } //printf("atof:%f\n",atof(digitStr.c_str())); s.push(atof(digitStr.c_str())); } } if(s.size()==1) printf("%0.1f\n",s.top()); else printf("ERROR\n"); return 0; } 
              
             
            
           
         
















版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/198278.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午3:26
下一篇 2026年3月26日 下午3:26


相关推荐

  • 京东薅羊毛全自动脚本_京东自动签到

    京东薅羊毛全自动脚本_京东自动签到双十一刚刚过,相信很多小伙伴也都剁手了。今年电商平台同样给出了很多优惠。有些优惠,比如红包,是靠运气来获得的,但是还有一些优惠是靠长期坚持才能获得。比如:签到、东东萌宠。每天都能坚持签到固然很棒,但是如果有脚本可以自动签到,那岂不是更美?

    2026年1月18日
    4
  • Webstorm关闭ESLint警告

    Webstorm关闭ESLint警告

    2022年4月30日
    61
  • 在线代理(Web ProxyServer)完全详解

    在线代理(Web ProxyServer)完全详解在线代理(WebProxy)原理可以简单的概述为:用户(A)-在线代理服务器(B)-目标网站(C),即:A向B发送浏览请求-B执行请求发送给C-C收到请求,回应。什么是在线代理  在线代理英文全称是(WebProxyServer),又称在线代理。代理服务器其功能就是代理网络用户去取得网络信息。形象的说:它是网络信息的中转站。在一般情况下,我们使用网络浏览器直接去连接其他

    2022年6月21日
    207
  • github最受欢迎语言(android开源框架)

    内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新ViewPager图表(Chart)菜单(Menu)浮动菜单对话框空白页滑动删除手势操作RecyclerViewCardColorDrawableSpinner布局模糊效果TabBarAppBar选择器(Picker)跑马灯日历时间主题样式ImageView通知聊天视图Head

    2022年4月13日
    55
  • js计时器

    js计时器本示例利用 JavaScript 设计一个倒计时显示牌 实现方法 用结束时间减去现在时 获取时间差 再利用数学方法从时间差中分别获取日 时 分 秒等信息 最后通过定时器设置每秒执行一次 实现实时更新 操作步骤 1 使用 newDate 获取当前时间 使用 new 调用一个带有参数的 Date 对象 定义结束的时间 endtime newDate 2020 8 8 使用 getTime 方法获取现在时和结束时距离 1970 年 1 月 1 日的毫秒数 然后 求两个时间差 把时间差转

    2026年3月18日
    2
  • 原则干货存起来

    关键的设计原则在开始设计之前,思考一下关键的原则,将会帮助你创建一个最小花费、高可用性和扩展性的架构。分离关注点,将应用划分为在功能上尽可能不重复的功能点。主要的参考因素就是最小化交互,高内聚、低

    2021年12月23日
    50

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号