中缀表达式转换为后缀表达式并求值

0.前言

之前用js实现的一个简易计算器有许多处理得不好的地方,抛开样式的问题,计算器最核心的计算部分直接使用了eval()函数,今天自己来实现计算部分,目标是获取一个算式字符串,并让计算机得出结果

在这之前,什么是中缀表达式,什么是后缀表达式??
今天特地查阅了一下《数据结构教程》

......在算术表达式中,运算符位于两个操作数中间的表达式称为中缀表达式(infix expression)...... 后缀表达式(postfix expression)或逆波兰表达式,就是在算术表达式中运算符在操作数的后面......

我们在日常生活中所使用的几乎都是中缀表达式。因为我们知道运算法则:括号内优先计算,先乘除后加减......计算机并不懂得我们的计算法则,后缀表达式才对它的胃口,转化好的后缀表达式已经事先考虑了优先级,不仅没有括号,而且位置越靠前的运算符越优先执行

在达到我们的目标前,我们事先准备三个“容器”:

var infixExp[];        //存放中缀表达式字符
var postfixExp[];      //存放后缀表达式字符
var tempExp[];         //存放临时字符

备注:上面的数组应该理解成,js中数组的pop()push()方法,下文会以出栈入栈来称呼。

1.从一个算式说起

4.25*(3+1)
看到这个算式,我们的第一个问题来了,如何将其正确地存入到infixExp里?
也就是说我们需要的是['4.25','*','(','3','+','1',')']而不是['4','.','2','5','*','(','3','+','1',')']

这里提供一种思路:

strTemp = "获取到的中缀表达式字符串";
function expSolve() {
    var i=0;
    var j=0;
    if(strTemp[0] == '-'){
        i = 1;
    }//对负数的处理

    for(; i<strTemp.length; i++){
        if(strTemp[i]=='('&&strTemp[i+1]=='-'){
            infixExp.push('(');
            j = i+1;
            i++;
            continue;
        }//对负数的处理

        if(strTemp[i]=='+'||strTemp[i]=='-'||strTemp[i]=='*'||strTemp[i]=='/'||strTemp[i]=='('||strTemp[i]==')'){
            if(strTemp.slice(j, i)!='')
                infixExp.push(strTemp.slice(j, i));
            infixExp.push(strTemp[i]);
            j=i+1;
        }
    }
    if(strTemp.slice(j)!='')
        infixExp.push(strTemp.slice(j));
}

扫描字符串的每个字符,当遇到运算符和括号时+-*/时,将该符号前部分和该符号依次插入到infixExp里,要做是否为空的判断(例如括号前可能会出现运算符)。最后剩下部分可能为运算符右侧的数也要插入数组。
可能还存在bug,慎用!一定有更好的方法

2.中缀表达式转换为后缀表达式

扫描infixExp中的每个元素:

  1. 遇到数,直接入栈postfixExp
  2. 遇到符号+,-,*,/时:
    • 如果此时临时栈tempExp为空,该符号直接入栈tempExp
    • 如果此时临时栈tempExp不为空,该符号优先级高于tempExp中的栈顶元素或栈顶元素为(时,入栈tempExp
    • 如果此时临时栈tempExp不为空,该符号优先级低于tempExp中的栈顶元素,将tempExp中的栈顶元素出栈,并入栈postfixExp,直到该符号优先级高于tempExp的栈顶元素或栈顶元素为(,才将该符号入栈。
  3. 遇到括号()时:
  • 遇到左括号(,直接入栈tempExp
  • 遇到右括号),右括号两个栈均不入,将tempExp中的栈顶元素出栈,并入栈postExp,直到遇到(。左括号只出栈但不进入postfixExp

左括号只有当遇到右括号时才出栈。
扫描完infixExp中的所有元素后,将tempExp中的元素依次弹出入栈postfixExp

中缀转后缀的实现(js)

function infixTopostfix() {
    var item;
    for (var i = 0; i < infixExp.length; i++) {
        if (!isNaN(infixExp[i])) {
            postfixExp.push(infixExp[i]);
        }
        else if (infixExp[i] == '+' || infixExp[i] == '-' || infixExp[i] == '*' || infixExp[i] == '/'||infixExp[i]=='('||infixExp[i]==')'){
            if (tempExp.length == 0) {
                tempExp.push(infixExp[i]);
            }
            else if (infixExp[i] == '*' || infixExp[i] == '/') {
                item = tempExp.pop();
                if (item == '*' || item == '/') {
                    postfixExp.push(item);
                }
                else {
                    tempExp.push(item);
                }
                tempExp.push(infixExp[i]);
            }
            else if (infixExp[i] == '+' || infixExp[i] == '-') {
                while (tempExp.length != 0) {
                    item = tempExp.pop();
                    if(item == '('){
                        tempExp.push(item);
                        break;
                    }
                    postfixExp.push(item);
                }
                tempExp.push(infixExp[i]);
            }
            else if(infixExp[i] == '('){
                tempExp.push(infixExp[i]);
            }
            else if(infixExp[i] == ')'){
                while (1) {
                    item = tempExp.pop();
                    if(item == '('){
                        break;
                    }
                    postfixExp.push(item);
                }
            }
        }
    }
    while (tempExp.length != 0) {
        postfixExp.push(tempExp.pop());
    }
}

3.后缀表达式的计算

扫描postfixExp中的每个元素:

  1. 遇到数,入栈tempExp
  2. 遇到符号+, -, *, /(后缀表达式一定不会含左右括号),从tempExp中依次取出两个数,并根据符号计算(后取出的数在符号左侧)。最后将该次结果入栈tempExp

扫描结束,tempExp中只剩一个元素, 即为最终结果!

后缀表达式的计算(js)

function postfixCalc() {
    for (var i = 0; i < postfixExp.length; i++) {
        if (!isNaN(postfixExp[i])) {
            tempExp.push(postfixExp[i]);
        }
        else {
            var strRight = opeStack.pop();
            var strLeft = opeStack.pop();
            switch (postfixExp[i]) {
                case '+':
                    opeStack.push(Number(strLeft) + Number(strRight));
                    break;
                case '-':
                    opeStack.push(Number(strLeft) - Number(strRight));
                    break;
                case '*':
                    opeStack.push(Number(strLeft) * Number(strRight));
                    break;
                case '/':
                    opeStack.push(Number(strLeft) / Number(strRight));
                    break;
                default:
                    alert("运算符号出错!");
            }
        }
    }
    var out = tempExp.pop();   //out即为最终结果
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容