KiMoGiGi 技术文集

不在乎选择什么,而在乎坚持多久……

IT博客 首页 联系 聚合 管理
  185 Posts :: 14 Stories :: 48 Comments :: 0 Trackbacks
1、需求

输入“表达式”字符串,输出计算结果。
如输入字符串:“1+4+7+(6*2*4+3+6-5)+2”
输出:66
假设运算只有+、-、×、/、(、)。

2、分析(参考节选(c#)数据结构与算法分析 --栈与队列

首先了解一下计算机是怎么计算类似于 1*(2+3)+4/2 这样的计算。对于大脑来说,这个算式简单到不能再简单了,但是电脑却不是如此,得让它明白那个先算,那个后算,以及哪些是操作符(运算符)哪些不是(例如括号)。

像我们经常使用的这种算式称为 中缀式 ,就是运算符都在两个操作符中间了,这种中缀式对于我们人脑来说,并不是顺序执行的,它可以从中间先开始,比如1+2*3 ,这正是电脑很难理解中缀式的原因,因为他总是顺序执行的。那么,我们就得把中缀式换成 后缀式(postfix)  或称作 逆波兰记法(reverse Polish notation)。

中缀式就是把运算符放到两个操作数后边,让算式保持顺序计算(对于我们人脑也是如此)。具体怎么放,大家看看下面的例子就明白了。
     
    

中缀式

后缀式

A+B+C-D A B+C+D-
A*B/C+D A B*C/D+
A*(B+C*D)+E A B C D * + * E +
(A+B)*C/(D+E-F) A B + C * D E + F - /
A+B*C+(D*E+F)*G A B C * + D E * F + G * +

结合上边的示例,会发现后缀式并没有描述优先级的括号。

使用后缀式,计算方向从左到右,遇到运算符,就计算运算符前2个数字,然后把结果替换这3者(2个数字和符号)位置。
如AB+C+D-
遇到第一个“+”,计算A+B,假设得到结果Z,后缀式就更新为
ZC+D-
如此类推,得到最后的结果。

3、实现代码
    public class ExpressionCalulate
    {
        
/// <summary>
        
/// 定义优先级
        
/// </summary>
        private static Dictionary<charint> Priority = new Dictionary<charint>();

        
static ExpressionCalulate()
        {
            Priority.Add(
'+'1);
            Priority.Add(
'-'1);
            Priority.Add(
'*'2);
            Priority.Add(
'/'2);
            Priority.Add(
'('3);
            Priority.Add(
')'3);
        }

        
/// <summary>
        
/// 计算表达式
        
/// </summary>
        
/// <param name="expression"></param>
        
/// <returns></returns>
        public static decimal Calulate(string expression)
        {
            
return CalulatePostfix(ConvertToPostfix(expression));
        }

        
/// <summary>
        
/// 计算后缀式字符串
        
/// </summary>
        
/// <param name="postfixExpression"></param>
        
/// <returns></returns>
        public static decimal CalulatePostfix(string postfixExpression)
        {
            Stack
<decimal> numbers = new Stack<decimal>();

            
string[] expressions = postfixExpression.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

            
foreach (string expression in expressions)
            {
                
if (expression.Length == 1 && Priority.ContainsKey(expression[0]))
                {
                    
//计算
                    decimal number2 = numbers.Pop();
                    
decimal number1 = numbers.Pop();
                    numbers.Push(EasyCaluate(number1, number2, expression[
0]));
                }
                
else
                {
                    numbers.Push(Convert.ToDecimal(expression));
                }
            }

            
return numbers.Pop();
        }

        
/// <summary>
        
/// 把表达式转换为后缀式
        
/// </summary>
        
/// <param name="expression"></param>
        
/// <returns></returns>
        public static string ConvertToPostfix(string expression)
        {
            
//初始化存储结果的后缀式数组
            char[] results = new char[expression.Length * 2];

            
//临时存放运算符
            Stack<char> s = new Stack<char>(expression.Length);

            
//当前存放results的索引值
            int currentResultCursorIndex = 0;

            
//遍历表达式
            foreach (char c in expression)
            {
                
if (!Priority.ContainsKey(c))
                {
                    
//如果不是运算符,直接输出结果
                    results[currentResultCursorIndex++= c;
                }
                
else
                {
                    
//如果是符号,先用' '隔开,方便区隔数字和运算符
                    results[currentResultCursorIndex++= ' ';

                    
//如果遇到右括号,即是出现一对括号,输出这对括号的运算符
                    if(c == ')' && s.Count > 0)
                    {
                        
char pop = default(char);
                        
while (pop != '(')
                        {
                            pop 
= s.Pop();
                            
if (pop != '(' && pop != ')')
                            {
                                results[currentResultCursorIndex
++= pop;
                                results[currentResultCursorIndex
++= ' ';
                            }
                        }
                        
continue;
                    }

                    
//判断当前运算符的优先级
                    int priority = Priority[c];
                    
int peekPriority = -1;

                    
//之前的运算符优先级
                    if (s.Count > 0)
                    {
                        peekPriority 
= Priority[s.Peek()];
                    }

                    
//当前与之前的运算符优先级做比较
                    if (priority > peekPriority || peekPriority == Priority['('])
                    {
                        
//如果当前优先级大于之前的运算符优先级或者遇到第一个括号,就先把运算符存起来
                        s.Push(c);
                    }
                    
else
                    {
                        
//否则,输出存储的运算符
                        while (s.Count > 0 && s.Peek() != '(')
                        {
                            results[currentResultCursorIndex
++= s.Pop();
                            results[currentResultCursorIndex
++= ' ';
                        }
                        s.Push(c);
                    }
                }
            }

            
//最后把所有运算符输出
            while (s.Count > 0)
            {
                results[currentResultCursorIndex
++= ' ';
                results[currentResultCursorIndex
++= s.Pop();
            }

            
//返回结果字符串
            return new string(results).TrimEnd('\0');
        }

        
private static decimal EasyCaluate(decimal d1, decimal d2, char opt)
        {
            
decimal result = 0;
            
switch (opt)
            {
                
case '+': { result = decimal.Add(d1, d2); break; }
                
case '-': { result = decimal.Subtract(d1, d2); break; }
                
case '*': { result = decimal.Multiply(d1, d2); break; }
                
case '/': { result = decimal.Divide(d1, d2); break; }
            }
            
return result;
        }

执行:
            Console.WriteLine(ExpressionCalulate.Calulate("1+2+3*3-2"));                //10

            Console.WriteLine(ExpressionCalulate.Calulate(
"1+4+7+(6*2*4+3+6-5)+2"));    //66

            Console.WriteLine(ExpressionCalulate.Calulate(
"1+4+7+(6*2*(4+3)+6-5)+2"));  //99

            Console.WriteLine(ExpressionCalulate.Calulate(
"1+4/7+(6*2*(4+3)+6-5)+2"));  //88.571428571428571428571428571429


4、参考:
(c#)数据结构与算法分析 --栈与队列

posted on 2009-02-18 23:11 KiMoGiGi 阅读(475) 评论(2)  编辑 收藏 引用 所属分类: Basic

评论

# re: 【原】实现表达式的计算[未登录] 2009-02-20 09:06 andy
强!见识了!  回复  更多评论
  

# re: 【原】实现表达式的计算 2009-02-20 13:57 KiMoGiGi
@andy
强个鸟……亡羊补牢罢了  回复  更多评论
  

只有注册用户登录后才能发表评论。