【JavaScript数据结构与算法】中缀表达式转后缀以及表达式求值

声明:本文章转载自作者http://godlikemeteor.com/ 的一篇,尊重原创。

题目描述

一个算术表达式的后缀表达式形式如下:
op1 op2 operator
使用两个栈,一个用来存储操作数,另外一个用来存储操作符,设计并实现一个 JavaScript 函数,该函数可以将中缀表达式转换为后缀表达式,然后利用栈对该表达式求值。
 

什么是中缀表达式和后缀表达式?

中缀表示法(或中缀记法)是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4)。与前缀表达式(例:+ 3 4)或后缀表达式(例:3 4 +)相比,中缀表达式不容易被电脑解析,但仍被许多程序语言使用,因为它符合人们的普遍用法。
与前缀或后缀记法不同的是,中缀记法中括号是必需的。计算过程中必须用括号将操作符和对应的操作数括起来,用于指示运算的次序。

后缀表示法 (逆波兰表示法)(Reverse Polish notation,RPN,或逆波兰记法),是一种是由波兰数学家扬·武卡谢维奇1920年引入的数学表达式方式,在逆波兰记法中,所有操作符置于操作数的后面,因此也被称为后缀表示法。逆波兰记法不需要括号来标识操作符的优先级。

逆波兰记法中,操作符置于操作数的后面。例如表达“三加四”时,写作“3 4 +”,而不是“3 + 4”。如果有多个操作符,操作符置于第二个操作数的后面,所以常规中缀记法的“3 - 4 + 5”在逆波兰记法中写作“3 4 - 5 +”:先3减去4,再加上5。使用逆波兰记法的一个好处是不需要使用括号。例如中缀记法中“3 - 4 5”与“(3 - 4)5”不相同,但后缀记法中前者写做“3 4 5 -”,无歧义地表示“3 (4 5 ) -”;后者写做“3 4 - 5 *”。
逆波兰表达式的解释器一般是基于堆栈的。解释过程一般是:操作数入栈;遇到操作符时,操作数出栈,求值,将结果入栈;当一遍后,栈顶就是表达式的值。因此逆波兰表达式的求值使用堆栈结构很容易实现,并且能很快求值。

首先我们要进行的就是中缀表达式和后缀表达式的转换。开始吧!

中缀表达式转后缀表达式

首先让我们来了解一下有关中缀转后缀的算法,大概有以下几个规则:
(1)当读到数字直接送至输出队列中;
(2)当读到运算符t时:
a.将栈中所有优先级高于或等于t的运算符弹出,送到输出队列中;
  b.t进栈;
(3)读到左括号时总是将它压入栈中;
(4)读到右括号时,将靠近栈顶的第一个左括号上面的运算符全部依次弹出,送至输出队列后,再丢弃左括号;
(5)中缀表达式全部读完后,若栈中仍有运算符,将其送到输出队列中。

符号的优先级

首先我们必须知道有关运算符优先级的问题,经过查阅,我们可以得到如下二维数组。

var sign = new Array();
                    // +   -   *    /    (    )   #
sign[0] = new Array('1','1','-1','-1','-1','1','1');   //+
sign[1] = new Array('1','1','-1','-1','-1','1','1');   //-
sign[2] = new Array('1','1','1','1','-1','1','1');     //*
sign[3] = new Array('1','1','1','1','-1','1','1');     ///
sign[4] = new Array('-1','-1','-1','-1','-1','0','');  //(
sign[5] = new Array('1','1','1','1','','1','1');       //)
sign[6] = new Array('-1','-1','-1','-1','-1','','0');  //#

先找横行,代表栈中的运算符,再找纵行,代表你想要比较的运算符,-1代表栈中符号优先级小于栈外符号优先级,栈外符号入栈;1代表栈中符号优先级大于栈外符号优先级,栈中符号弹出,直到栈中符号优先级比栈外符号小,栈外符号入栈;0代表优先级一样,暂时不进行考虑。

符号的比较函数

弄清楚符号的优先级,我们就可以对两个符号进行比较,具体实现代码如下:

function Sign(a,b){
    var str = ['+','-','*','/','(',')','#'];
    var str1;
    var str2;
    for(var i=0;i<7;i++){
        if(a == str[i]){
            str1 = i;
        }
        if(b == str[i]){
            str2 = i;
        }
    }
    var count = sign[str1][str2];
    return count;
}

将符号关系转换为数字更有利于我们之后的判断,接下来就是我们的正题来了。

构造一个栈

跟上一篇一样,我们需要构造一个基本的数据结构—-栈,来实现这个项目,不同的是,我们新增加了一个函数peek()来获取栈顶的第一个元素。下面是代码实现:

function Stack(){
    this.dataStore = [];
    this.top = 0;
    this.push = push;
    this.pop = pop;
    this.length = length;
    this.peek = peek;
    this.clear = clear;
}
function push(element){
    this.dataStore[this.top] = element;
    this.top++;
}
function pop(){
    return this.dataStore[--this.top];;
}
function peek(){
    return this.dataStore[this.top-1];
}
function clear(){
    this.top = 0;
}
function length(){
    return this.top;
}

构造好一个栈之后,我们就需要进行最重要的步骤了,构造转换函数。

中缀表达式转换函数

中缀表达式转后缀表达式跟C语言不同,JavaScript是一种弱类型语言,它的实现更加的灵活,首先我们在栈中先压入#,令最后一个元素能够弹出,然后我们构造如下函数:

function Change(item){
    var str = item;
    var stack = new Stack();                       //构造一个栈
    stack.push("#");                               //将#压入栈中
    var outStack = new Array();                    //构造一个队列
    var small = "";
    var flog = 0;
    for(var i=0;i= 0){
                    do{
                        outStack.push(stack.pop());
                    }while(Sign(stack.peek(),str[i])>0);
                    stack.push(str[i]);
                }
            }
        }
    }
    console.log(outStack);
}

然后我们就可以输入中缀表达式进行计算了,注意在控制台计算的时候在中缀表达式的后面加一个#号,防止栈内符号未完全弹出的情况出现。

测试实例:Change('1+2*(3-1+2)-3#');
示例输出:1231-2+*+3- 注意,输出的是一个队,也就是一个数组,本样例是为了方便这样测试的。

后缀表达式的计算

相对于中缀表达式转后缀表达式,后缀表达式的计算就简单多了,直接上代码,在注释中进行讲解。

function suffix(item){
    var str = item;
    var outStack = new Stack();
    var small = "";
    var flog = 0;
    for(var i=0;i

测试实例:suffix('1231-2+*+3-')
示例输出:6
注意后缀表达式的计算无需将符号入栈,只需要弹出数字进行运算即可。

中缀表达式的计算

中缀表达式的计算原理如果你理解了中缀转后缀、后缀运算那么很快你就能够理解,这个例子也是直接在代码中进行讲解。

function Infix(item){
    var str = item;
    var stack = new Stack();
    stack.push("#");                       //将#字压入栈
    var outStack = new Array();
    var small = "";
    var flog = 0;
    for(var i=0;i= 0){
                    do{
                        var b1 = parseFloat(outStack.pop());
                        var b2 = parseFloat(outStack.pop());
                        var a3 = stack.pop();
                        switch(a3){
                            case'+':outStack.push(b2 + b1);
                            break;
                            case'-':outStack.push(b2 - b1);
                            break;
                            case'*':outStack.push(b2 * b1);
                            break;
                            case'/':outStack.push(b2 / b1);
                            break;
                        }
                    }while(Sign(stack.peek(),str[i])>0);
                    stack.push(str[i]);
                }
            }
        }
    }
    console.log(outStack.pop().toFixed(5));   //将小数位数控制在5位小数,结束运算。
}

 

你可能感兴趣的:(【JavaScript数据结构与算法】中缀表达式转后缀以及表达式求值)