c++生成表达式二叉树并正向打印输出/显示

  • Post author:
  • Post category:其他


思路:先把输入的中缀表达式转化为后缀表达式,再通过后缀表达式来建立一颗表达式二叉树,最后正向打印二叉树。

正向打印二叉树的思路已写在_initPos_函数中,具体参见注释。


#include <bits/stdc++.h>
using namespace std;`在这里插入代码片`
struct node
{//节点类
    char value;
    node *left;
    node *right;
    int level = 0;//节点的行坐标
    int column = 10;//节点的列坐标 默认为10
    node(char _a, node *_l = NULL, node *_r = NULL)
    {//节点类的构造函数
        value = _a;
        left = _l;
        right = _r;
    }
};
class Tree
{//二叉树类
private:
    stack<node*> lake;//用于在构建二叉树时保存子树的栈
    node *myroot;//树根
    char draw[100][100];//画板 默认时全都为空格字符 
    int Cmin = 100; //二叉树的最左边的y坐标
    int Cmax = 0;//二叉树最右边的y坐标 列坐标
    int Lmax = 0;//二叉树的最大x坐标 即行坐标
public:
    Tree()
    {//二叉树的构造函数
        _initD_();//初始化画板数组
        myroot = NULL;//初始化根节点
    }

    void _initD_(){//画板数组的初始化函数
        for (int i = 0; i < 100; i++){
            for (int j = 0; j < 100; j++){
                draw[i][j] = ' ';//默认全都置空
            }
        }
    }

    node *front()
    {//头节点的访问函数
        return myroot;
    }

    void creatTreeByMidExpression(string midexp)
    {//通过中缀表达式建立二叉树
        string s = TransToAfexp(midexp);//把中缀表达式转化为后缀表达式 便于之后的处理
        for (int i = 0; i < s.length(); i++)
        {
            node* father = NULL;
            node *left = NULL;
            node *right = NULL;
            if (IsOperator(s[i]))
            {
                right = lake.top();
                lake.pop();
                left = lake.top();
                lake.pop();
            }
            father = new node(s[i], left, right);
            lake.push(father);
        }//程序逻辑:从头至尾遍历后缀表达式  遇到运算符 就将运算符前面的两个元素与运算符一起建立一个子树 然后将这个子树压入待处理栈 当遇到一个运算符前面的两个字符中有运算符时 就讲栈顶的元素作为当前所访问的运算符的左子树或右子树
        myroot = lake.top();//最后直接返回栈顶的元素就是要求的表达式二叉树
    }

    string TransToAfexp(string midexp)
    {//将中缀表达式转化为后缀表达式的函数
        string res;
        map<char, int> Operator;
        Operator['+'] = 1;
        Operator['-'] = 1;
        Operator['*'] = 2;
        Operator['/'] = 2;
        Operator['('] = 3;
        Operator['0'] = 0;//定义各种运算符的优先级 保证栈顶的元素的优先级一定是最大的 具体实现方法和教材采取的方法大同小异
        stack<char> OpratorStack;
        OpratorStack.push('0');
        for (int i = 0; i < midexp.length(); i++)
        {
            if (!IsOperator(midexp[i]))
            {
                res += midexp[i];
            }
            else
            {
                if (midexp[i] == '(')
                {
                    OpratorStack.push(midexp[i]);
                    continue;
                }
                if (midexp[i] == ')')
                {
                    while (OpratorStack.top() != '(')
                    {
                        res += OpratorStack.top();
                        OpratorStack.pop();
                    }
                    OpratorStack.pop();
                    continue;
                }
                if (OpratorStack.top() == '(')
                {
                    OpratorStack.push(midexp[i]);
                    continue;
                }
                if (Operator[midexp[i]] > Operator[OpratorStack.top()])
                    OpratorStack.push(midexp[i]);
                else
                {
                    while (Operator[midexp[i]] <= Operator[OpratorStack.top()] && OpratorStack.top() != '0')
                    {
                        res += OpratorStack.top();
                        OpratorStack.pop();
                    }
                    OpratorStack.push(midexp[i]);
                }
            }
        }
        while (OpratorStack.top() != '0')
        {
            res += OpratorStack.top();
            OpratorStack.pop();
        }
        return res;//返回处理好的后缀表达式
    }

    bool IsOperator(char s)
    {//判断一个字符是否是运算符
        return s == '+' || s == '-' || s == '*' || s == '/' || s == ')' || s == '(';
    }

    void display(node *root)
    {//调试用的显示函数
        if (root != NULL)
        {
            display(root->left);
            display(root->right);
            cout << root->value << endl;
        }
    }

    void _initPos_(node* root){//初始化各个节点的位置
        if (root != NULL){
            if (root->left){
                root->left->level = root->level + 1;
                if (2*root->left->level > Lmax) Lmax = 2*root->left->level;
                root->left->column = root->column - (5 - root->left->level);
                if (Cmin > 2*root->left->column) Cmin = 2*root->left->column;
            } 
            _initPos_(root->left);
            if (root->right){
                if (2*root->right->level > Lmax) Lmax = 2*root->right->level;
                root->right->level = root->level + 1;
                root->right->column = root->column + (5 - root->right->level);
                if (Cmax < 2*root->right->column) Cmax = 2*root->right->column;
            }
            _initPos_(root->right);
        }
    }//程序逻辑:越接近于根节点的位置 列坐标的变化越大 而对于接近根的程度可以用行坐标来描述 对于一个节点 左儿子的列坐标比父节点的列坐标小 而右儿子的列坐标比父节点的列坐标要大 根据深度来做出相应的调整即可

    void printPos(node* root){ //中序遍历整棵树 输出各个节点的坐标和值
        if (root != NULL){
            printPos(root->left);
            cout << "(" << root->level << "," << root->column << ") " << root->value                                                                   << endl;
            printPos(root->right);
        }
    }

    void _initPrint_(node* root){ //初始化画板数组 将各个节点的坐标映射到画板数组中去 为了让效果更明显 坐标进行了放大处理
        if (root != NULL){
            _initPrint_(root->left);
            draw[2*root->level][2*root->column] = root->value;
            _initPrint_(root->right);
        }
    }

    void print(){//打印二叉树
        for (int j = 0; j <= Lmax; j++){
            for (int i = Cmin; i <= Cmax; i++){
                cout << draw[j][i];
            }
            cout << endl;
        }
    }
};
int main()
{
    Tree s;
    s.creatTreeByMidExpression("a*b+(c-d)/e-f");
    s._initPos_(s.front());
    cout << "position of each node:" << endl;
    s.printPos(s.front());
    s._initPrint_(s.front());
    cout << "expression tree of a*b+(c-d)/e-f is:\n";
    s.print();
    system("pause");
    return 0;
}



版权声明:本文为tomly2020原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。