[经典算法] 中序式转后序式/前序式

题目说明:

平常所使用的运算式,主要是将运算元放在运算子的两旁,例如a+b/d这样的式子,这称之为中序(Infix)表示式,对于人类来说,这样的式子很容易理 解,但由于电脑执行指令时是有顺序的,遇到中序表示式时,无法直接进行运算,而必须进一步判断运算的先后顺序,所以必须将中序表示式转换为另一种表示方 法。
可以将中序表示式转换为后序(Postfix)表示式,后序表示式又称之为逆向波兰表示式(Reverse polish notation),它是由波兰的数学家卢卡谢维奇提出,例如(a+b)*(c+d)这个式子,表示为后序表示式时是ab+cd+*。

题目解析:

用手算的方式来计算后序式相当的简单,将运算子两旁的运算元依先后顺序全括号起来,然后将所有的右括号取代为左边最接近的运算子(从最内层括号开始),最后去掉所有的左括号就可以完成后序表示式,例如:

a+b*d+c/d   =>    ((a+(b*d))+(c/d)) -> bd*+cd/+

如果要用程式来进行中序转后序,则必须使用堆叠,演算法很简单,直接叙述的话就是使用回圈,取出中序式的字元,遇运算元直接输出,堆叠运算子与左括号, ISP>ICP的话直接输出堆叠中的运算子,遇右括号输出堆叠中的运算子至左括号。

以下是虚拟码的运算法,\0表示中序式读取完毕:

Procedure Postfix(infix) [
    Loop [
        op = infix(i)
        case [
            :x = ‘\0‘:
                while (stack not empty)
                     // output all elements in stack
                end
                return
             :x = ‘(‘:
                 // put it into stack
             :x is operator:
                  while (priority(stack[top]) >=
                         priority(op)) [
                       // out a element from stack
                  ]
                  // save op into stack
             :x = ‘)‘:
                   while ( stack(top) != ‘(‘ ) [
                       // out a element from stack
                   ]
                   top = top - 1  // not out ‘(
             :else:
                   // output current op
        ]
        i++;
    ]
]

例如(a+b)*(c+d)这个式子,依演算法的输出过程如下:


OP


STACK


OUTPUT


(


(


-


a


(


a


+


(+


a


b


(+


ab


)


-


ab+


*


*


ab+


(


*(


ab+


c


*(


ab+c


+


*(+


ab+c


d


*(+


ab+cd


)


*


ab+cd+


-


-


ab+cd+*

如果要将中序式转为前序式,则在读取中序式时是由后往前读取,而左右括号的处理方式相反,其余不变,但输出之前必须先置入堆叠,待转换完成后再将堆叠中的 值由上往下读出,如此就是前序表示式。

程序代码:

#include <iostream>
#include <stack>
#include <algorithm>
#include <gtest/gtest.h>
using namespace std;

int GetOperatorPrior(char value)
{
    int nResult = 0;
    switch(value)
    {
    case ‘+‘:
    case ‘-‘:
        {
            nResult = 1;
        }
        break;

    case ‘*‘:
    case ‘/‘:
        {
            nResult = 2;
        }
        break;
    }

    return nResult;
}

bool ConvertToPostfix(const string& infixExp, string& postfixExp)
{
    postfixExp.clear();
    stack<int> Operators;
    for (string::size_type i = 0; i < infixExp.size(); ++i)
    {
        char cValue = infixExp[i];
        switch(cValue)
        {
        case ‘(‘:
            {
                Operators.push(cValue);
            }
            break;

        case ‘)‘:
            {
                while(!Operators.empty() &&
                    (Operators.top() != ‘(‘))
                {
                    postfixExp += Operators.top();
                    Operators.pop();
                }

                Operators.pop();
            }
            break;

        case ‘+‘:
        case ‘-‘:
        case ‘*‘:
        case ‘/‘:
            {
                while (!Operators.empty() &&
                    (GetOperatorPrior(Operators.top()) >= GetOperatorPrior(cValue)) )
                {
                    postfixExp += Operators.top();
                    Operators.pop();
                }

                Operators.push(cValue);
            }
            break;

        default:
            postfixExp += cValue;
            break;
        }
    }

    while(!Operators.empty())
    {
        postfixExp += Operators.top();
        Operators.pop();
    }

    return true;
}

bool ConvertToPrefix(const string& infixExp, string& prefixExp)
{
    prefixExp.clear();
    int* Stack = new int[infixExp.size()+1];
    int  nTop = 0;

    for (int i = infixExp.size() - 1; i >= 0; --i)
    {
        char cValue = infixExp[i];
        switch (cValue)
        {
        case ‘)‘:
            {
                Stack[++nTop] = ‘)‘;
            }
            break;

        case ‘(‘:
            {
                while (nTop &&
                    Stack[nTop] != ‘)‘)
                {
                    prefixExp += Stack[nTop];
                    nTop--;
                }

                nTop--;
            }
            break;

        case ‘+‘:
        case ‘-‘:
        case ‘*‘:
        case ‘/‘:
            {
                while (nTop &&
                    GetOperatorPrior(Stack[nTop]) >= GetOperatorPrior(cValue))
                {
                    prefixExp += Stack[nTop];
                    nTop--;
                }

                Stack[++nTop] = cValue;
            }
            break;

        default:
            prefixExp += cValue;
            break;
        }
    }

    while (nTop)
    {
        prefixExp += Stack[nTop--];
    }

    reverse(prefixExp.begin(), prefixExp.end());

    return true;
}

TEST(Algo, tInFixPostfix)
{
    //
    //    Postfix Convert
    //

    // a+b*d+c/d => abd*+cd/+
    string strResult;
    ConvertToPostfix("a+b*d+c/d",strResult);
    ASSERT_EQ("abd*+cd/+",strResult);

    // (a+b)*c/d+e => ab+c*d/e+
    ConvertToPostfix("(a+b)*c/d+e",strResult);
    ASSERT_EQ("ab+c*d/e+",strResult);

    // ((a)+b*(c-d)+e/f)*g => abcd-*+ef/g*
    ConvertToPostfix("((a)+b*(c-d)+e/f)*g",strResult);
    ASSERT_EQ("abcd-*+ef/+g*",strResult);

    //
    //    Prefix Convert
    //

    // a+b*d+c/d => +a+*bd/cd
    ConvertToPrefix("a+b*d+c/d",strResult);
    ASSERT_EQ("+a+*bd/cd",strResult);

    // (a+b)*c/d+e => +*+ab/cde
    ConvertToPrefix("(a+b)*c/d+e",strResult);
    ASSERT_EQ("+*+ab/cde",strResult);

    // ((a)+b*(c-d)+e/f)*g => *+a+*b-cd/efg
    ConvertToPrefix("((a)+b*(c-d)+e/f)*g",strResult);
    ASSERT_EQ("*+a+*b-cd/efg",strResult);

}
时间: 2024-10-09 11:25:17

[经典算法] 中序式转后序式/前序式的相关文章

算法学习 - 表达树的建立(后缀表达式法),树的先序遍历,中序遍历,后序遍历

表达树就是根据后缀表达式来建立一个二叉树. 这个二叉树的每个叶子节点就是数,真祖先都是操作符. 通过栈来建立的,所以这里也会有很多栈的操作. 树的先序遍历,中序遍历,后序遍历的概念我就不讲了,不会的自行百度,不然也看不懂我的代码. 下面是代码: // // main.cpp // expressionTree // // Created by Alps on 14-7-29. // Copyright (c) 2014年 chen. All rights reserved. // #includ

已知二叉树的前序遍历、中序遍历或者中序遍历、后序遍历求二叉树结构的算法

二叉树中的前序遍历是先访问根结点,再访问左子树,右子树. 中序遍历是先访问左子树,再是根结点,最后是右子树. 后序遍历是先访问左子树,再是右子树,最后是根结点. 算法思路是先根据前序遍历的第一个结点或者后序遍历的最后一个结点,查找对应在中序遍历中的位置,就可以确定左子树包含的元素和右子树包含的元素,最后通过递归来实现就可以了. 二叉树的表示形式为 //二叉树的结构表示为 class TreeNode { int val; TreeNode left; TreeNode right; TreeNo

算法进化历程之“根据二叉树的先序和中序序列输出后序序列”

巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 前不久在看到一个作业"根据二叉树的先序和中序序列输出后序序列",当时我参考<数据结构与算法(C语言)习题集>上的做法,先根据先中序序列确定一颗二叉树,然后后序遍历二叉树输出后序序列. 函数采用了递归算法,利用函数传入的先序和中序序列的左右边界,确定要处理的序列段,生成相应的二叉树. 基本思路是,把该段先序序列的第一个元素作为当前二叉树的根结点,然后在中序序列找到根结点.根结点

中序表达式转后序表式式

中序表达式转后序表式式: 将中序表达式所有括号补全,然后将所有运算符向右移出无匹配的第一个右括号,去掉括号即为后序表式式 举例: 原式:a+b*(c+d/e) 补全括号:(a+(b*(c+(d/e)))) 操作符右移:(a(b(c(de)/)+)*)+ 去掉括号:abcde/+*+ 中序表达式转前序表式式: 将中序表达式所有括号补全,然后将所有运算符向左移出无匹配的第一个左括号,去掉括号即为前序表式式 举例: 原式:a+b*(c+d/e) 补全括号:(a+(b*(c+(d/e)))) 操作符右移

根据 中序遍历 和 后序遍历构造树(Presentation)(C++)

好不容易又到周五了,周末终于可以休息休息了.写这一篇随笔只是心血来潮,下午问了一位朋友PAT考的如何,顺便看一下他考的试题,里面有最后一道题,是关于给出中序遍历和后序遍历然后求一个层次遍历.等等,我找一下链接出来...... 1127. ZigZagging on a Tree (30):https://www.patest.cn/contests/pat-a-practise/1127 突然想起以前学数据结构的时候如果给出一个中序遍历和一个后序遍历然后让你画出树的结构或求出先序遍历之类的题目,

二叉树(15)----由中序遍历和后序遍历重建二叉树,递归方式

1.二叉树定义 typedef struct BTreeNodeElement_t_ { void *data; } BTreeNodeElement_t; typedef struct BTreeNode_t_ { BTreeNodeElement_t *m_pElemt; struct BTreeNode_t_ *m_pLeft; struct BTreeNode_t_ *m_pRight; } BTreeNode_t; 2.由中序遍历和后序遍历重建二叉树 中序遍历中,根节点总是位于左右子树

72 中序遍历和后序遍历树构造二叉树

原题网址:https://www.lintcode.com/problem/construct-binary-tree-from-inorder-and-postorder-traversal/description 描述 根据中序遍历和后序遍历树构造二叉树 你可以假设树中不存在相同数值的节点 您在真实的面试中是否遇到过这个题?  是 样例 给出树的中序遍历: [1,2,3] 和后序遍历: [1,3,2] 返回如下的树: 2 /  \ 1    3 标签 二叉树 思路:要建立二叉树,首先要建立根

二叉树、前序遍历、中序遍历、后序遍历

一.树 在谈二叉树前先谈下树和图的概念 树:不包含回路的连通无向图(树是一种简单的非线性结构) 树有着不包含回路这个特点,所以树就被赋予了很多特性 1.一棵树中任意两个结点有且仅有唯一的一条路径连通 2.一棵树如果有n个结点,那它一定恰好有n-1条边 3.在一棵树中加一条边将会构成一个回路 4.树中有且仅有一个没有前驱的结点称为根结点 在对树进行讨论的时候将树中的每个点称为结点, 根结点:没有父结点的结点 叶结点:没有子结点的结点 内部结点:一个结点既不是根结点也不是叶结点 每个结点还有深度,比

二叉树——前序遍历、中序遍历、后序遍历、层序遍历详解(递归非递归)

前言 前面介绍了二叉排序树的构造和基本方法的实现.但是排序遍历也是比较重要的一环.所以笔者将前中后序.和层序遍历梳理一遍. 了解树的遍历,需要具有的只是储备有队列,递归,和栈.这里笔者都有进行过详细介绍,可以关注笔者数据结构与算法专栏.持续分享,共同学习. 层序遍历 层序遍历.听名字也知道是按层遍历.我们知道一个节点有左右节点.而每一层一层的遍历都和左右节点有着很大的关系.也就是我们选用的数据结构不能一股脑的往一个方向钻,而左右应该均衡考虑.这样我们就选用队列来实现. 对于队列,现进先出.从根节