算术表达式之前缀、中缀、后缀表达式转化

概述

前缀、中缀、后缀表达式一般是根据操作符的位置来确定的,在我们去理解什么是前缀表达式和后缀表达式之前,可以先看下中缀表达式是什么?看如下的例子:
(3+4)* 5— 6 这就是我们正常一般看到的表达式。

虽然中缀表达式符合我们人的日常思维习惯,但是计算机在存储中缀表达式时,需要使用树这种数据结构(思考下,这里是因为包括括号的概念),如果表达式过于复杂,那么树的高度会变得很高,大大增加了时间复杂度和空间复杂度。如果转换成线性结构,那么效率将变得高很多,所以需要将中缀表达式先转换成前缀或者后缀表达式,然后依靠栈这种线性数据结构来进行计算。一般计算机中存储基本是转成后缀表达式,然后来进行求值。

前缀表达式

前缀表达式又称为波兰表达式,他的特点是运算符位于操作数之前。
举例说明,(3+4) x 5 - 6 转为前缀表达式就是 - X + 3 4 5 6 .

中缀表达式如何转化成前缀表达式

      *(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
     * (2) 从右至左扫描中缀表达式;
     * (3) 遇到操作数时,将其压入S2;
     * (4) 遇到运算符时,比较其与S1栈顶运算符的优先级:
     * (4-1) 如果S1为空,则直接将此运算符入栈;
     * (4-2) 否则,若优先级比栈顶运算符的较高或相等(后缀表达式中是较高,没有相等)或栈顶运算符为右括号“)”,
        也将运算符压入S1;
     * (4-3) 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
     * (5) 遇到括号时:
     * (5-1) 如果是右括号“)”,则直接压入S1;
     * (5-2) 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到右括号为止,此时将这一对括号丢弃
     * (6) 重复步骤(2)至(5),直到表达式的最左边;
     * (7) 将S1中剩余的运算符依次弹出并压入S2;
     * (8) 依次弹出S2中的元素并输出,结果即为中缀表达式对应的前缀表达式。
     * (后缀表达式这里要将字符串反转输出,这里直接输出即可)

下面是进行转化的步骤:



这个是代码实现:

private static String zhong2Qian(String expression) {

        //(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
        Stack<Character> ops = new Stack<>();
        Stack<Character> resultValues = new Stack<>();

        //(2) 从右至左扫描中缀表达式;这里是先反转字符串再遍历其字符数组
        expression = reverseString(expression);
        char[] chars = expression.toCharArray();
        for (char theChar : chars) {
            //(3) 遇到操作数时,将其压入S2;
            if (Character.isDigit(theChar))
                resultValues.push(theChar);
            //(4) 遇到运算符时,比较其与S1栈顶运算符的优先级:
            else if (theChar == '+' || theChar == '-' || theChar == '*' || theChar == '/') {
                    dealTheChar(ops, resultValues, theChar);

            }
            //(5-1) 如果是右括号“)”,则直接压入S1;
            else if (theChar == ')')
                ops.push(theChar);
            //(5-2) 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到右括号为止,此时将这一对括号丢弃;
            else if (theChar == '(') {
                char opsChar = ops.pop();
                while (opsChar != (')')) {
                    resultValues.push(opsChar);
                    opsChar = ops.pop();
                }
            }
        }

        //(7)将S1中剩余的运算符依次弹出并压入S2;
        while (!ops.empty()) {
            resultValues.push(ops.pop());
        }

        //(8) 依次弹出S2中的元素并输出,结果即为中缀表达式对应的前缀表达式。
        // (后缀表达式这里要将字符串反转输出,这里直接输出即可)
        StringBuilder exp = new StringBuilder();
        while (!resultValues.empty())
            exp.append(resultValues.pop());

        return exp.toString();
    }

后缀表达式

后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作符之后,转化时时从左边往右扫描表达式的。这也是计算机中主要的存储算术表达式的方式。

中缀表达式如何转化成前缀表达式

/**
     * 将中缀表达式转换为后缀表达式:
     与转换为前缀表达式相似,遵循以下步骤:
     * (1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
     * (2) 从左至右扫描中缀表达式;
     * (3) 遇到操作数时,将其压入S2;
     * (4) 遇到运算符时,比较其与S1栈顶运算符的优先级:
     * (4-1) 如果S1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
     * (4-2) 否则,若优先级比栈顶运算符的高,也将运算符压入S1(注意转换为前缀表达式时是优先级较高或相同,
            而这里则不包括相同的情况);
     * (4-3) 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
     * (5) 遇到括号时:
     * (5-1) 如果是左括号“(”,则直接压入S1;
     * (5-2) 如果是右括号“)”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到左括号为止,此时将这一对括号丢弃;
     * (6) 重复步骤(2)至(5),直到表达式的最右边;
     * (7) 将S1中剩余的运算符依次弹出并压入S2;
     * (8) 依次弹出S2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式(转换为前缀表达式时不用逆序)。
     * @return
     */

计算会比较复杂,所以这里用两个例子:

(1): 中缀表达式 1+((2 + 3) x 4)-5


结果为: 1 2 3 + 4 x + 5 -

(2): 中缀表达式 ( 3+ 4) x 5 -6

代码实现如下:

 private static String zhong2hou(String expression) {

        //(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
        Stack<Character> ops = new Stack<>();
        Stack<Character> resultValues = new Stack<>();

        char[] chars = expression.toCharArray();

        //(2) 从左至右扫描中缀表达式;
        for (char theChar : chars){
            //(3) 遇到操作数时,将其压入S2;
            if (Character.isDigit(theChar))
                resultValues.push(theChar);
            //(4) 遇到运算符时,比较其与S1栈顶运算符的优先级:
            else if (theChar == '+' || theChar == '-' || theChar == '*' || theChar == '/')
                dealTheChar(ops,resultValues,theChar);
            //(5) 遇到括号时:
            //(5-1) 如果是左括号“(”,则直接压入S1;
            else if (theChar == '(')
                ops.push(theChar);
            //(5-2) 如果是右括号“)”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到左括号为止,此时将这一对括号丢弃;
            else if (theChar == ')'){
                char opsChar  = ops.pop();
                while (opsChar != '(') {
                    resultValues.push(opsChar);
                    opsChar  = ops.pop();
                }
            }
        }

        //(7) 将S1中剩余的运算符依次弹出并压入S2;
        while (!ops.isEmpty())
            resultValues.push(ops.pop());
        
        //(8) 依次弹出S2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式(转换为前缀表达式时不用逆序)。
        StringBuilder exp = new StringBuilder();
        while (!resultValues.empty())
            exp.append(resultValues.pop());

        return reverseString(exp.toString());
    }

private static void dealTheChar(Stack<Character> ops, Stack<Character> resultValues, char theChar) {
        //(4-1) 如果S1为空,则直接将此运算符入栈;
        if (ops.empty()) {
            ops.push(theChar);
            return;
        }
        char popTheChar = ops.pop().charValue();
        //(4-1) 如果S1为空,或栈顶运算符为右括号“(”,则直接将此运算符入栈;
        //(4-2) 否则,若优先级比栈顶运算符的高,也将运算符压入S1(注意转换为前缀表达式时是优先级较高或相同,而这里则不包括相同的情况)
        if (popTheChar == '(' || getPriorityValue(theChar) > getPriorityValue(popTheChar)) {
            ops.push(popTheChar);
            ops.push(theChar);
        }
        //(4-3) 否则,将S1栈顶的运算符弹出并压入到S2中
        else {
            resultValues.push(popTheChar);
            //,再次转到(4-1)与S1中新的栈顶运算符相比较;
            dealTheChar(ops, resultValues, theChar);
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容