递归输出去括号的中缀表达式

1. 定义一棵简化的 Linked Binary Tree

1.1. Binary Tree Node

假设元素类型为 std::string,
include 如下几个头文件.

#include <iostream>
#include <string>

先定义 Node.
元素类型为 std::string

struct binaryTreeNode
{
    std::string element;
    binaryTreeNode* leftChild, * rightChild;
    // Constructor 1.
    binaryTreeNode() { leftChild = rightChild = nullptr; }
    // Constructor 2.
    binaryTreeNode(const std::string& theElement) :
        element(theElement), leftChild(nullptr), rightChild(nullptr) {}
    // Constructor 3.
    binaryTreeNode(const std::string& theElement, binaryTreeNode* theLeft, binaryTreeNode* theRight) :
        element(theElement), leftChild(theLeft), rightChild(theRight) {}
};

1.2. Linked Binary Tree

再利用 binaryTreeNode 写一个简单的 linked binary tree.
这里初始化树的方法是用户输入以空格划分的树的前序遍历结果, 同时指出何处为 nullptr.

e.g.
假设指定了 "#" 表示 nullptr, 即将 "#" 传入形参 stop.
用户输入: + * 1 # # - 2 # # 3 # # / 4 # # 5 # # ;
所表示的表达式应为: 1*(2-3)+4/5 ;
其中 "+" 为树的 root.

class linkedBinaryTree
{
public:
    // Constructor, initialize {root} with nullptr.
    linkedBinaryTree() : root(nullptr) {};
    // Get private member {root}.
    binaryTreeNode* getRoot() const { return root; }
    // Initialize the tree with pre-order-output string of an expression;
    // {stop} indicates nullptr.
    void createTree(binaryTreeNode* root, const std::string& stop)
    {
        std::string element;
        std::cin >> element;
        if (element == stop) { return; }
        root = new binaryTreeNode(element);
        createTree(root->leftChild, stop);
        createTree(root->rightChild, stop);
    }
private:
    binaryTreeNode* root;
};

2. 输出去括号的中缀表达式

2.1. 两个辅助函数

判断元素是否为运算符.

inline bool isOperator(const std::string& op)
{
    if (op == "+" || op == "-" || op == "*" || op == "/") { return true; }
    return false;
}

返回运算符优先级.
运算符优先级按照这种特殊的方式设定值,
是考虑到将返回值 除以 2 后能形成一层新的 加减乘除 的划分

inline int getOperPri(const std::string& op)
{
    switch (op[0]) {
    case '+': return 2;
    case '-': return 3;
    case '*': return 4;
    case '/': return 5;
    default: return -1;
    }
}

2.2. 递归输出中缀表达式同时去括号

为了去括号, 递归的时候还要传入一个 prePri, 表示上层的优先级.
顺手枚举个 direction 类型, 表示本层递归是前一层走哪个方向进来的.
具体实现并不复杂, 注释得比较清晰了.
关于加减遇到负数的变号, 实现起来也比较轻松, 大致方法写在注释里了.

enum direction { start = -1, left, right };
void infix(binaryTreeNode* t, int prePri, direction dir)
{
    if (t == nullptr) { return; }

    int curPri = 0;    // Mark current operator priority (0 if element is a number).
    bool flag = true;   // <true> to print brakets; <false> not to.

    if (isOperator(t->element)) { // IF current element is an operator.
        curPri = getOperPri(t->element); // Get current priority.
        if (dir == left) {
            if (curPri / 2 >= prePri / 2) { flag = false; }
        } else if (dir == right) {
            if (curPri > prePri || curPri == prePri && !(prePri & 1)) { flag = false; }
        } else { // IF {dir} is start, do not print brakets.
            flag = false; 
        }
    } else { // IF element is a number.
        // IF the number is possitive, does not need brackets.
        if (std::stoi(t->element) > 0) { flag = false; }
        // If you wish, you can also add some codes here to change '+' and '-' operator when meeting a negative number.
        // e.g. When the current element is <-3>, and {prePri} indicates that the operator before is '+';
        // Send a signal back to last level with return value in order to change '+' to '-'.
    }

    if (flag) { std::cout << '('; }
    // Recurse to left subtree.
    infix(t->leftChild, curPri, left);
    // Print element.
    std::cout << t->element;
    // Recurse to right subtree.
    infix(t->rightChild, curPri, right);
    if (flag) { std::cout << ')'; }
}

3. 方法使用示例

有 2.2. 中定义的枚举类型 direction 的情况下,
函数 void infix(binaryTreeNode* t, int prePri, direction dir) 的第四个参数必须是 direction 的变量 start (表示递归第一层);
第三个参数 prePristart 的限制下, 调用函数是可以传入任意值 (因为 prePri 在递归的第一层不会参与比较).

int main()
{
    std::string stop;
    std::cin >> stop; // User define {stop} to indicate nullptr.
    linkedBinaryTree tree; // Define an empty tree.
    tree.createTree(tree.getRoot(), stop); // Create the tree.
    // Output the result.
    // The 4th parameter must be <start>, while the 3rd does not matter.
    infix(tree.getRoot(), 0, start);
    system("pause");
}

4. 后记

潇潇给我的这题还挺有意思的.
深知目前自己的能力还非常不足. 哈哈.
不过 <欢迎来到实力至上主义的教室> 小说真的好看呢.
一之濑真好呀, 和天使一样!
第二季动画做的... 呃呃... 感觉不太好.
04/08/2022 17:37


posted @ 2022-08-04 17:45  JamesNULLiu  阅读(67)  评论(0编辑  收藏  举报