算法题打卡day17 | 513.找树左下角的值、112. 路径总和、113.路径总和ii、106.从中序与后序遍历序列构造二叉树、105.从前序与中序遍历序列构造二叉树

513. 找树左下角的值 - 力扣(LeetCode)

状态:想到了要用到回溯法,但是困在了之前的套路,不知从何下手。

该题也用到了回溯法,但是值得注意的是回溯法不一定需要进行路径的记录。更进一步说的话,回溯法本身和路径记录没有关系,因为利用递归进行遍历本身就包含了回溯的过程,区别在于普通的遍历考虑的是当前节点和子节点的关系,而回溯则要求还要考虑和其父节点的关系。所以说,回溯法中,具体是怎么控制前进,抑或是怎么得到答案,全在于如何发现可以终止和进行回溯的条件是什么。代码如下:

class Solution {
public:
    int max_depth = INT_MIN;
    int res;

    void traversal(TreeNode* root, int depth){
        if(root->left == nullptr && root->right == nullptr){
            if(depth > max_depth){
                max_depth = depth;
                res = root->val;
            }
            return;
        }

        if(root->left){
            traversal(root->left, depth+1);
        }

        if(root->right){
            traversal(root->right, depth+1);
        }
    }

    int findBottomLeftValue(TreeNode* root) {
        int depth = 0;
        traversal(root, depth);
        return res;
    }
};

 112. 路径总和 - 力扣(LeetCode)

状态:Debug后AC。

和上一题类似,注意节点回溯的吃入和吐出的逻辑,代码如下:

class Solution {
public:
    int sum_num = 0, target = 0;
    bool res = false;

    void traversal(TreeNode* node){
        if(res || node == nullptr) {return;}

        if(node->left == nullptr && node->right == nullptr){
            if(sum_num+node->val == target){
                res =  true;
            }else{
                return;
            }
        }
        if(node->left){
            sum_num += node->val;
            traversal(node->left);
            sum_num -= node->val;
        }
        if(node->right){
            sum_num += node->val;
            traversal(node->right);
            sum_num -= node->val;
        }
        return;
    }

    bool hasPathSum(TreeNode* root, int targetSum) {
        target = targetSum;
        traversal(root);
        return res;
    }
};

113. 路径总和 II - 力扣(LeetCode)

状态:Debug后AC。

和路径总和在思路上一样,但是多了一些判断操作,代码如下:

class Solution {
public:
    int target = 0;
    int sum(vector<int>& path){
        int res = 0;
        for(auto num : path){
            res += num;
        }
        //cout << res << endl;
        return res;
    }

    void traversal(TreeNode* node, vector<int>& path, vector<vector<int>>& res){
        if(node->left == nullptr && node->right == nullptr){
            //cout << node->val << ": ";
            path.emplace_back(node->val);
            int temp = sum(path);
            if(temp == target){
                res.emplace_back(path);
            }
            path.pop_back();
            return;
        }
        if(node->left){
            path.emplace_back(node->val);
            traversal(node->left, path, res);
            path.pop_back();
        }
        if(node->right){
            path.emplace_back(node->val);
            traversal(node->right, path, res);
            path.pop_back();
        }
        return;
    }

    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<vector<int>> res;
        if(root == nullptr) return res;

        vector<int> path;
        //path.emplace_back(root->val);
        target = targetSum;
        traversal(root, path, res);

        return res;
    }
};

 106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

状态:思路容易想到,但是具体的实现需要一步一步拆解。

通过后序遍历数组的最后一位来确定当前的根节点,接着确定根节点在中序遍历数组中的位置(索引),根据索引可以将中序遍历数组划分为左右两个子数组。通过中序遍历的子数组可以同样地确定后序遍历中左右子数组的划分。这其中要注意的是左右子数组的边界问题。 代码如下:

class Solution {
private:
    TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
        if (postorder.size() == 0) return NULL;

        // 后序遍历数组最后一个元素,就是当前的中间节点
        int rootValue = postorder[postorder.size() - 1];
        TreeNode* root = new TreeNode(rootValue);

        // 叶子节点
        if (postorder.size() == 1) return root;

        // 找到中序遍历的切割点
        int delimiterIndex;
        for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) break;
        }

        // 切割中序数组
        // 左闭右开区间:[0, delimiterIndex)
        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
        // [delimiterIndex + 1, end)
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );

        // postorder 舍弃末尾元素
        postorder.resize(postorder.size() - 1);

        // 切割后序数组
        // 依然左闭右开,注意这里使用了左中序数组大小作为切割点
        // [0, leftInorder.size)
        vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
        // [leftInorder.size(), end)
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());

        root->left = traversal(leftInorder, leftPostorder);
        root->right = traversal(rightInorder, rightPostorder);

        return root;
    }
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.size() == 0 || postorder.size() == 0) return NULL;
        return traversal(inorder, postorder);
    }
};

 105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

状态:上一题方法中有太多vector的创建,可以通过改成索引的方法来优化。

代码如下:

class Solution {
private:
        TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd) {
        if (preorderBegin == preorderEnd) return NULL;

        int rootValue = preorder[preorderBegin]; // 注意用preorderBegin 不要用0
        TreeNode* root = new TreeNode(rootValue);

        if (preorderEnd - preorderBegin == 1) return root;

        int delimiterIndex;
        for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) break;
        }
        // 切割中序数组
        // 中序左区间,左闭右开[leftInorderBegin, leftInorderEnd)
        int leftInorderBegin = inorderBegin;
        int leftInorderEnd = delimiterIndex;
        // 中序右区间,左闭右开[rightInorderBegin, rightInorderEnd)
        int rightInorderBegin = delimiterIndex + 1;
        int rightInorderEnd = inorderEnd;

        // 切割前序数组
        // 前序左区间,左闭右开[leftPreorderBegin, leftPreorderEnd)
        int leftPreorderBegin =  preorderBegin + 1;
        int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin; // 终止位置是起始位置加上中序左区间的大小size
        // 前序右区间, 左闭右开[rightPreorderBegin, rightPreorderEnd)
        int rightPreorderBegin = preorderBegin + 1 + (delimiterIndex - inorderBegin);
        int rightPreorderEnd = preorderEnd;

        root->left = traversal(inorder, leftInorderBegin, leftInorderEnd,  preorder, leftPreorderBegin, leftPreorderEnd);
        root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, preorder, rightPreorderBegin, rightPreorderEnd);

        return root;
    }

public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (inorder.size() == 0 || preorder.size() == 0) return NULL;

        // 参数坚持左闭右开的原则
        return traversal(inorder, 0, inorder.size(), preorder, 0, preorder.size());
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是的,可以修改CreateHCode()算法来计算哈夫曼树的带权路径长度。一种常见的方法是在CreateHCode()算法中添加一个参数,用于记录当前节点的深度和权,然后在遍历哈夫曼树的过程中计算每个叶子节点的带权路径长度,最后将所有叶子节点的带权路径长度相加即可得到哈夫曼树的带权路径长度。 具体实现方法如下: 1. 在CreateHCode()算法中添加一个参数,用于记录当前节点的深度和权。 2. 在遍历哈夫曼树的过程中,如果遇到叶子节点,则计算该叶子节点的带权路径长度,并将其保存到一个变量中。 3. 遍历完所有叶子节点后,将所有叶子节点的带权路径长度相加即可得到哈夫曼树的带权路径长度。 下面是一个示例代码,用于演示如何修改CreateHCode()算法来计算哈夫曼树的带权路径长度: ```c++ struct HTNode { char symbol; int weight; int parent; int leftChild; int rightChild; }; void CreateHCode(HTNode huffTree[], int n, string huffCode[], int& wpl) { for (int i = 0; i < n; i++) { int currentNode = i; int currentDepth = 0; int currentWeight = huffTree[i].weight; string currentCode = ""; while (huffTree[currentNode].parent != -1) { int parent = huffTree[currentNode].parent; if (huffTree[parent].leftChild == currentNode) { currentCode = "0" + currentCode; } else { currentCode = "1" + currentCode; } currentNode = parent; currentDepth++; } huffCode[i] = currentCode; wpl += currentWeight * currentDepth; } } ``` 在上面的代码中,我们添加了一个名为wpl的参数,用于记录哈夫曼树的带权路径长度。在遍历哈夫曼树的过程中,如果遇到叶子节点,则计算该叶子节点的带权路径长度,并将其累加到wpl中。最后,wpl就是哈夫曼树的带权路径长度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值