表达树构造

表达树构造
表达树是一个二叉树的结构,用于衡量特定的表达。所有表达树的叶子都有一个数字字符串值。而所有表达树的非叶子都有另一个操作字符串值。

给定一个表达数组,请构造该表达的表达树,并返回该表达树的根。

逆波兰式转换为表达式树相对比较简单,只需要一个栈保存node,遇到操作符是,弹出前两个node,分别为左右子节点。

解题思路

1.将中缀表达式转换为逆波兰式(或称后缀表达式)

2. 逆波兰式转换为表达式树

(a+b)*(c*(d+e);

对该树进行后序遍历得到后缀表达式
ab+cde+**;
这里实现的是如何根据一个后缀表达式,构造出其相应的表达式树。
算法思想:其实很简单,主要就是栈的使用。算法时间复杂度是O(n),n是后缀表达式长度。
从前向后依次扫描后缀表达式,如果是操作数就建立一个单节点树,并把其指针压入栈。如果是操作符,则
建立一个以该操作符为根的树,然后从栈中依次弹出两个指针(这2个指针分别指向2个树),作为该树的
左右子树。然后把指向这棵树的指针压入栈。直到扫描完后缀表达式。
最后栈中就会只有一个指针。这个指针指向构造的表达式树的根节点。


/**
 * Definition of ExpressionTreeNode:
 * class ExpressionTreeNode {
 * public:
 *     string symbol;
 *     ExpressionTreeNode *left, *right;
 *     ExpressionTreeNode(string symbol) {
 *         this->symbol = symbol;
 *         this->left = this->right = NULL;
 *     }
 * }
 */

class Solution {
private:
    bool isOp(string str)
    {
        if((str.compare("+") == 0) || (str.compare("-") == 0) ||
           (str.compare("*") == 0) || (str.compare("/") == 0))
        {
            return true;
        }
        return false;
    }
    
    int compareOrder(string str1, string str2)
    {
        int order1 =  ((str1.compare("*") == 0) || (str1.compare("/") == 0)) ? 1 : 0;
        int order2 =  ((str2.compare("*") == 0) || (str2.compare("/") == 0)) ? 1 : 0;
        
        return order1 - order2;
    }
    // 将中缀表达式转换为后缀表达式(逆波兰式)
    vector<string> convertToRPN(vector<string> &expression){
        vector<string> rpnList;
        stack<string> opStack;
        for(int i = 0; i < expression.size(); i++){
            string now = expression[i];
            if(now.compare("(") == 0){
                opStack.push(now);
            }
            else if(now.compare(")") == 0){
                while(!opStack.empty() &&
                        opStack.top().compare("(") != 0){
                    rpnList.push_back(opStack.top());
                    opStack.pop();
                }
                if(!opStack.empty()) {
                    opStack.pop();
                }
            }
            else if(isOp(now)){
                while(!opStack.empty() &&
                        isOp(opStack.top()) &&
                        compareOrder(now, opStack.top()) <= 0){
                    rpnList.push_back(opStack.top());
                    opStack.pop();
                }
                opStack.push(now);
            }
            else {
                rpnList.push_back(now);
            }
        }
        while(!opStack.empty()){
            rpnList.push_back(opStack.top());
            opStack.pop();
        }
        return rpnList;
    }
    
    
    ExpressionTreeNode* buildFromRPN(vector<string> &expression){
        if(expression.size() == 0){
            return NULL;
        }
        stack<ExpressionTreeNode*> s;
        for(int i = 0; i < expression.size(); i++){
            string now = expression[i];
            if(isOp(now)){
                ExpressionTreeNode * right = s.top();
                s.pop();
                ExpressionTreeNode * left = s.top();
                s.pop();
                ExpressionTreeNode * node = new ExpressionTreeNode(now);
                node->left = left;
                node->right = right;
                s.push(node);
            }
            else{
                s.push(new ExpressionTreeNode(now));
            }
        }
        return s.top();
    }
public:
    /**
     * @param expression: A string array
     * @return: The root of expression tree
     */
    ExpressionTreeNode* build(vector<string> &expression) {
        // write your code here
        if(expression.size() == 0) {
            return NULL;
        }
        vector<string> rpe = convertToRPN(expression);
        return buildFromRPN(rpe);
    }
};


### 解决方案概述 在PTA平台上解决构造哈夫曼的问题涉及多个方面,包括理解哈夫曼的概念、掌握其构建方法及其应用。通过解析不同场景下的需求,可以更好地应对这类题目。 #### 构建哈夫曼的具体过程 对于给定的一组字符及其出现频次,按照特定规则逐步合并节点直到形成一棵完整的二叉即为所求的哈夫曼。在此过程中需遵循以下原则: - 当存在多个相同权重的候选节点时,应依据它们首次出现在输入序列中的位置来决定谁将成为新的父节点的孩子;较早出现的那个会被选作左侧孩子[^3]。 - 左边分支用`0`表示而右边分支则用`1`表示,在最终得到的编码串里,越靠近叶子部分代表更短的有效位数[^4]。 ```cpp #include <iostream> #include <vector> using namespace std; struct Node { char ch; int weight; bool operator<(const Node& rhs)const{ return this->weight != rhs.weight ? this->weight < rhs.weight : this->ch < rhs.ch; } }; void huffmanTree(vector<Node>& nodes){ while(nodes.size() > 1){ sort(nodes.begin(),nodes.end()); Node left = nodes[0]; Node right = nodes[1]; Node parent {'\0',left.weight + right.weight}; // Remove the two smallest elements and add their sum as a new node. nodes.erase(nodes.begin()); nodes.erase(nodes.begin()); nodes.push_back(parent); } } ``` 此段代码展示了如何基于一组带有权重值的对象列表创建哈夫曼的核心逻辑。这里假设每个对象都含有一个字符成员变量用于存储实际意义的数据项以及整型成员保存频率计数值。排序操作确保每次迭代都能找到当前集合内具有最低两个权值的元素并将其组合起来成为更高层次上的单一实体直至整个数组只剩下一个最高级别的根节点为止。 #### 输出哈夫曼编码 一旦完成了上述步骤建立起完整的哈夫曼之后,就可以遍历这棵从而获取各个叶节点关联字符相应的编码字符串了。通常情况下我们会采用递归的方式访问每一个非终端顶点并向左转时记录下'0'向右转记下'1'最后到达末端的时候便能获得一条完整的路径描述也就是目标字符独一无二的二进制表达形式[^2]。 ```cpp void generateCodes(Node* root, string str, map<char,string> &huffmancode) { if (!root) return ; if (isLeaf(root)) huffmancode[root->ch]=str; generateCodes(root->left , str+"0", huffmancode); generateCodes(root->right, str+"1", huffmancode); } bool isLeaf(Node* root) {return !(root->left || root->right);} ``` 这段辅助函数负责沿着已建立好的哈夫曼向下探索,并沿途累积由‘0’和‘1’构成的方向指示符,当触及到底部某一片叶子时就将积累下来的路线信息同该处携带的实际字符一起存入映射表中以便后续查询使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值