【数据结构—我来动手】支持四则混合运算的计算器

32 篇文章 0 订阅

【数据结构—我来动手】支持四则混合运算的计算器

 1.给出两个数,用户再指定操作符,要求计算结果,这实现起来很容易;

    2.多个数,但只涉及同一优先级的操作符,做起来也很容易;

    3.多个数,不同优先级的操作符,怎么办呢?

   想想就头痛,不过还好前人已经为我们留下了很多解决这个问题的方法。通过逆波兰表达式是解决这个问题很流行的一种方式。

 

   一、什么是逆波兰表达式?

    我们一般使用的表达式,形如1+2*3,被称为中缀表达式,转换为后缀表达式即为:1 2 3 * +,而后缀表达式也就是我们所说的逆波兰表达式。

    逆波兰表达式计算起来非常方便:遇操作数入栈,遇操作符则弹出栈顶两个元素进行计算并将结果推入栈中,直至结束。上面的表达式的计算过程可以用下图表示:

 

    也正因为逆波兰的方便性,使它成为了计算器的普遍实现方式。

   二、四则混合运算计算器

    既然思路已经清晰了,那么我们的计算器可以分两步走:

    1.将表达式转换为逆波兰形式

    2.逆波兰表达式求值。

    

     生成逆波兰表达式:

    将一般中缀表达式转换为逆波兰表达式有如下转换过程:

    (1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
    (2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。
    (3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。
    (4)如果不是数字,该字符则是运算符,此时需比较优先关系。
    做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将栈顶的运算符从栈中弹出,直到栈顶运算符的优先级低于当前运算符,将该字符入栈。
    (5)重复上述操作(3)-(4)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。

            而上面提到的运算符优先级如下:

 

操作符#^*,/,%+,- ()
isp(栈内优先级)075318
icp(栈外优先级)064281

 

    下面是程序化算法流程:
    1、建立运算符栈stackOperator用于运算符的存储,压入'/0'。
    2、预处理表达式,正、负号前加0(如果一个加号(减号)出现在最前面或左括号后面,则该加号 (减号)为正负号) 。
    3、顺序扫描表达式,如果当前字符是数字(优先级为0的符号),则直接输出该数字;如果当前字符为运算符或括号(优先级不为0的符号),则判断第4点 。
    4、若当前运算符为'(',直接入栈;
    若为')',出栈并顺序输出运算符直到遇到第一个'(',遇到的第一个'('出栈但不输出;
    若为其它,比较stackOperator栈顶元素与当前元素的优先级:
    如果 栈顶元素 >= 当前元素,出栈并顺序输出运算符直到 栈顶元素 < 当前元素,然后当前元素入栈;
    如果 栈顶元素 < 当前元素,直接入栈。
    5、重复第3点直到表达式扫描完毕。
    6、顺序出栈并输出运算符直到栈顶元素为'/0'。 

 

    a.预处理表达式,对于一元+,-在前面加0:

         ///   <summary>
        
///  处理正负号
         
///   </summary>
        
///   <param name="exp"></param>
        
///   <returns></returns>
         private   static   string  FormatExp( string  exp)
        {
            var result 
=  exp.Trim().Replace( "   " "" );

            
if  (result[ 0 ==   ' + '   ||  result[ 0 ==   ' - ' )
            {
                result 
=   " 0 "   +  result;
            }
            
for  (var i  =   0 ; i  <  result.Length  -   1 ; i ++ )
            {
                
if  (result[i]  ==   ' ( '   &&  (result[i  +   1 ==   ' + '   ||  result[i  +   1 ==   ' - ' ))
                {
                    result 
=  result.Substring( 0 , i  +   1 +   " 0 "   +  result.Substring(i  +   1 );
                }
            }

            
return  result;
        }

 

    b.将格式化后的表达式转换为逆波兰表达式

         ///   <summary>
        
///  将解析过后的表达式转换为逆波兰表达式(各个语义单元存放在列表中)
        
///   </summary>
        
///   <param name="tokens"></param>
        
///   <returns></returns>
         private   static  List < string >  ToRPNExp(List < string >  tokens)
        {
            var result 
=   new  List < string > ();
            var opStack 
=   new  Stack < string > ();


            opStack.Push(
" /0 " );

            
for  (var i  =   0 ; i  <  tokens.Count; i ++ )
            {
                
if  (IsOperator(tokens[i]))
                    result.AddRange(HandleOperator(opStack, tokens[i]));
                
else  result.Add(tokens[i]);
            }

            
while  (opStack.Peek()  !=   " /0 " )
            {
                result.Add(opStack.Pop());
            }
            
return  result;
        }
        
///   <summary>
        
///  根据操作符的优先级决定入栈还是出栈
        
///   </summary>
        
///   <param name="st"></param>
        
///   <param name="op"></param>
        
///   <returns></returns>
         private   static  List < string >  HandleOperator(Stack < string >  st,  string  op)
        {
            var result 
=   new  List < string > ();

            
if  (op  ==   " ( " )
            {
                st.Push(op);
            }
            
else   if  (op  ==   " ) " )
            {
                
while  (st.Peek()  !=   " ( " )
                {
                    result.Add(st.Pop());
                }
                st.Pop();
            }
            
else
            {
                var priority1 
=  IcpPriority[op];
                var priority2 
=  IspPriority[st.Peek()];


                
while  (priority2  >=  priority1)
                {
                    result.Add(st.Pop());
                    priority2 
=  IspPriority[st.Peek()];
                }
                st.Push(op);
            }
            
return  result;
        }

 

    求值:

     ///   <summary>
        
///  进行计算
        
///   </summary>
        
///   <param name="tokens"></param>
        
///   <returns></returns>
         private   static   double  DoCalc(List < string >  tokens)
        {
            var st 
=   new  Stack < double > ();

            
foreach  (var token  in  tokens)
            {
                
if  ( ! IsOperator(token))
                {
                    
// 操作数入栈
                    st.Push( double .Parse(token));
                }
                
else
                {
                    
// 操作符从栈顶取出两个元素进行计算,并将结果推入栈中
                    var operand1  =  st.Pop();
                    var operand2 
=  st.Pop();

                    
switch  (token)
                    {
                        
case   " + " :
                            st.Push(operand2 
+  operand1);
                            
break ;
                        
case   " - " :
                            st.Push(operand2 
-  operand1);
                            
break ;
                        
case   " * " :
                            st.Push(operand2 
*  operand1);
                            
break ;
                        
case   " / " :
                            st.Push(operand2 
/  operand1);
                            
break ;
                        
case   " ^ " :
                            st.Push(Math.Pow(operand2, operand1));
                            
break ;

                    }
                }
            }
            
// 最终栈顶元素即为表达式的值
             return  st.Pop();
        }

 
    就这样,一个完整的计算器就完成了,不会的朋友现在也会了吧?

    计算器下载    

    源代码下载

 

    【参考资料】http://baike.baidu.com/view/552648.htm?fr=ala0_1

          数据结构与程序设计:C++语言描述

 

来自博客园 Frog http://www.cnblogs.com/MythYsJh/archive/2010/04/01/1702224.html

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值