栈的应用-计算表达式

问题:给你一个表达式(当然是字符串),求这个表达式的值。
在javascript中有一个简单的方法可以轻松实现,就是eval,代码如下:
var expr = '-(-1)*(2+(7*5))';
var ret = eval(expr);
alert(ret);
要是我们自己写一个这样的函数,那就得用到“栈”了。所谓栈,就是一种特殊的线性表,它遵循“后入先出”的原则,每次只能从栈顶一个一个的取出。这里用JavaScript语言模拟栈,代码如下:
// 模拟一个栈
function Stack(){
    var stack = {},
        top = -1;
    return {
        // 栈是否为空
        isEmpty : function(){
            return (top == -1 ? true : false);
        },
        // 栈(顶)当前值
        curr : function(){
            return (top >= 0 ? stack[top] : null);
        },
        // 入栈操作
        push : function(o){
            ++top;
            stack[top] = o;
        },
        // 出栈,会返回出栈的值
        pop : function(){
            if(top < 0){
                return null;
            }
            var temp = stack[top];
            delete stack[top];
            --top;
            return temp;
        }
    };
}
下面运用栈来进行表达式的运算操作,首先是要搞清楚思路:
  1. 取两个栈,一个用来存表达式中的数字,另一个存符号(+ 、- 、* 、/);
  2. 从表达式开头按字符扫描;若是数字,就入数栈;若是符号,则要分情况处理:
    • 若当前符号优先级大于符号栈栈项的优先级,当前符号入符号栈;
    • 否则,一直进行如下中间运算操作:
      • 依次从数字栈中弹出两个数,分别记为num1, num2;
      • 从符号栈中弹出一个符号,记为#
      • 进行简单的四则运算:ret = num2 # num1;
      • 并将得到的结果ret再入数字栈;
    • 完成中间运算操作后,并将当前符号入符号栈;
  3. 表达式扫描完成后,进行上述中间操作,直到符号栈为空;
上面的思路仅仅是考虑最简单的四则运算,并没有考虑符号为括号时的处理。还有一些细节问题,下面用代码来具体实现(JavaScript语言):
function calc(expr){
    // 数字栈、符号栈、表达式索引、表达式的长度、拼接数字的中间变量
    var nums = new Stack(),
        opers = new Stack(),
        index = 0,
        max = expr.length,
        num_temp = '';
    
    // 是否为符号(运算符或括号)
    var isOper = function(ch){
        if(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')'){
            return true;
        }
        return false;
    };

    // 运算优先级
    var prec = function(oper){
        if(oper == '+' || oper == '-'){
            return 1;
        }else if(oper == '*' || oper == '/'){
            return 2;
        }
    };
    
    // 两个数的简单基本四则运算
    var getResult = function(num1, num2, oper){
        switch(oper){
            case '+':
                return num2 + num1;
                break;
            case '-':
                return num2 - num1;
                break;
            case '*':
                return num2 * num1;
                break;
            case '/':
                return num2 / num1;
                break;
        }
    };
    
    /**
     * 中间运算
     * 依次从数字栈中弹出两个数分别记为num1, num2
     * 从符号栈中弹出一个符号
     * 然后运算,并将得到的结果再 入 数字栈
     */
    var middle_calc = function(){
        num1 = nums.pop();
        // 兼容若表达式中第一个字符为'-',比如'-(-1)*2+(7-5)';
        // 但不进行处理也没问题
        //num2 = nums.pop();
        num2 = nums.pop() || 0;
        oper = opers.pop();
        var ret = getResult(num1, num2, oper);
        nums.push(ret);
        //console.log(num2);
    };
    
    // 扫描表达式,进行入栈操作
    while(true){
        // 当前扫描字符
        var ch = expr[index];

        // 当前扫描字符为数字
        if(!isNaN(ch)){

            // 数字进行拼接(以形成多位数)
            num_temp += ch;

        // 当前扫描字符为符号
        }else if(isOper(ch)){
            
            // 当前符号的前一个数字 入 数字栈,
            // 并清空拼接数字的中间变量以便拼接下一个数字
            if(num_temp !== ''){
                nums.push(num_temp * 1);
                num_temp = '';
            }
            
            // 符号栈为空 或 扫描的当前符号为'(' 或 当符号栈中的栈顶为'(',
            // 则扫描的当前符号 入 符号栈
            if(opers.isEmpty() || ch == '(' || (opers.curr() == '(')){
                opers.push(ch);
                
            // 符号栈非空且扫描的当前符号为 运算符(+、-、*、/)时
            }else if(ch !== '(' && ch !== ')'){

                // 扫描的当前符号优先级 大于 符号栈中的栈顶的优先级
                // 则扫描的当前符号 入 符号栈
                if( prec(ch) > prec(opers.curr()) ){
                    opers.push(ch);
                    
                }else{
                
                    // 扫描的当前符号优先级若一直 不大于 符号栈中的栈顶的优先级
                    // 则一直进行中间运算
                    while(prec(ch) - prec(opers.curr()) <= 0){
                        middle_calc();
                    }

                    // 中间运算完成后,扫描的当前符号 入 符号栈
                    opers.push(ch);
                }
            
            // 扫描的当前符号为')'时
            }else if(ch == ')'){
                
                // 若符号栈中的栈顶不为'(',就一直进行中间运算
                while(opers.curr() !== '('){
                    middle_calc();
                }

                // 中间运算完成后,'(' 出栈
                oper = opers.pop(); 
            }

        }
// 最后一个数字入数字栈
     if(num_temp !== ''){
         nums.push(num_temp * 1);
         num_temp = '';
     }
// 表达式索引加1,以扫描表达式中下一个字符 ++index; // 表达式索引到了表达式最末,则完成扫描,退出循环 if(index > max){ break; } } // 扫描完成后的最后运算 while(!opers.isEmpty()){ middle_calc(); } // 数字栈中的最后一个就是表达式的最终运算结果 return nums.pop(); }
验证结果:
var expr = '-(-1)*(2+(7*5))';
console.log(calc(expr));
成功!

posted on 2012-10-26 11:13  27_Man  阅读(828)  评论(0)    收藏  举报