算法训练营 Day 16

1、另一棵树的子树

class Solution {
public:
    //判断两棵树是否相同
    bool compare(TreeNode* left, TreeNode* right) {
        if (left == nullptr && right == nullptr)
            return true;
        if (left == nullptr || right == nullptr || left->val != right->val)
            return false;
        return compare(left->left, right->left) && compare(left->right, right->right);
    }

    //一棵树是另一棵树的子树,有下面三种情况
    //1、两棵树完全相同
    //2、一棵树的左子树和另一棵树相同
    //3、一棵树的右子树和另一棵树相同
    bool isSubtree(TreeNode* root, TreeNode* subRoot) {
        if (root == nullptr && subRoot == nullptr)
            return true;
        if (root == nullptr || subRoot == nullptr)
            return false;
        if (root->val == subRoot->val && compare(root, subRoot))
            return true;
        else
            return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);       
    }
};

2、找树左下角的值

使用递归法:

class Solution {
public:
    int maxDepth = INT_MIN;
    int result;
    void traversal(TreeNode* root, int depth) {
        if (root->left == nullptr && root->right == nullptr) {
            if (depth > maxDepth) {
                maxDepth = depth;
                result = root->val;
            }
            return;
        }
        if (root->left) {
            depth++;
            traversal(root->left, depth);
            depth--; // 回溯
        }
        if (root->right) {
            depth++;
            traversal(root->right, depth);
            depth--; // 回溯
        }
    }
    int findBottomLeftValue(TreeNode* root) {
        traversal(root, 0);
        return result;
    }
};

精简后:

class Solution {
public:
    int maxDepth = -1; //-1即可
    int result;
    void traversal(TreeNode* root, int depth) {
        if (root->left == nullptr && root->right == nullptr) {
            if (depth > maxDepth) {
                maxDepth = depth;
                result = root->val;
            }
            return;
        }
        if (root->left) {
            traversal(root->left, depth + 1); // 隐藏着回溯
        }
        if (root->right) {
            traversal(root->right, depth + 1); // 隐藏着回溯
        }
    }
    int findBottomLeftValue(TreeNode* root) {
        traversal(root, 0);
        return result;
    }
};

层序迭代:

class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != nullptr) que.push(root);
        int result = 0; //储存最终结果
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                TreeNode* node = que.front();
                que.pop();
                if (i == 0) result = node->val; // 记录每一行的第一个元素,最后更新为最后一行的第一个元素
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }
};

3、路径总和 I

递归法:

class Solution {
private:
    bool traversal(TreeNode* cur, int count) {
        if (!cur->left && !cur->right) {
            if (count == 0)
                return true; // 遇到叶子节点,并且计数为0
            else
                return false; // count != 0 的情况
        }
        if (cur->left) { // 左
            count -= cur->left->val; // 递归,处理节点;
            if (traversal(cur->left, count)) return true; //递归要层层向上返回
            count += cur->left->val; // 回溯,撤销处理结果
        }
        if (cur->right) { // 右
            count -= cur->right->val; // 递归,处理节点;
            if (traversal(cur->right, count)) return true;
            count += cur->right->val; // 回溯,撤销处理结果
        }
        return false;
    }

public:
    bool hasPathSum(TreeNode* root, int sum) {
        if (root == nullptr) return false;
        return traversal(root, sum - root->val);
    }
};

精简后的:

class Solution {
public:
    bool hasPathSum(TreeNode* root, int sum) {
        if (!root) return false;
        if (!root->left && !root->right && sum == root->val) 
            return true;
        return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
    }
};

前序迭代:

class Solution {
public:
    bool haspathsum(TreeNode* root, int sum) {
        if (root == nullptr) return false;
        // 此时栈里要放的是pair<节点指针,路径数值>
        stack<pair<TreeNode*, int>> st;
        st.emplace(root, root->val); //也可改成 st.push({root, root->val});
        while (!st.empty()) {
            pair<TreeNode*, int> node = st.top();
            st.pop();
            // 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
            if (!node.first->left && !node.first->right && sum == node.second) return true;

            // 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
            if (node.first->right) {
                st.emplace(node.first->right, node.second + node.first->right->val); //node.second是之前路径数值之和,node.first->right->val是当前结点的数值
            }

            // 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
            if (node.first->left) {
                st.emplace(node.first->left, node.second + node.first->left->val);
            }
        }
        return false;
    }
};

4、路径总和 II

递归法:

class Solution {
private:
    vector<vector<int>> result; //二维结果数组
    vector<int> path; //一维结果数组
    // 递归函数不需要返回值,因为我们要遍历整个树
    void traversal(TreeNode* cur, int count) {
        if (!cur->left && !cur->right) {
            if (count == 0) {  // 遇到了叶子节点且找到了和为sum的路径
                result.push_back(path);
                return;
            } else {  // 遇到叶子节点而没有找到合适的边,直接返回
                return;
            }
        }

        if (cur->left) { // 左 (空节点不遍历)
            path.push_back(cur->left->val);
            traversal(cur->left, count - cur->left->val);    // 递归
            path.pop_back();                // 回溯
        }
        if (cur->right) { // 右 (空节点不遍历)
            path.push_back(cur->right->val);
            traversal(cur->right, count - cur->right->val);   // 递归
            path.pop_back();                // 回溯
        }
    }

public:
    vector<vector<int>> pathSum(TreeNode* root, int sum) {
        if (root == nullptr) return result; //此时result为空,不用.clear()
        path.push_back(root->val); // 把根节点放进路径
        traversal(root, sum - root->val);
        return result;
    }
};

5、从中序与后序遍历序列构造二叉树

递归法:

class Solution {
public:
    TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) {
        if (postorder.empty()) return nullptr; //后序为空,那中序肯定也为空,只用判断一个就行

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

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

        // 找到中序遍历的切割点
        int delimiterIndex = 0; //在for循环外面还要使用,所以定义在外面
        for (; 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 = buildTree(leftInorder, leftPostorder);
        root->right = buildTree(rightInorder, rightPostorder);

        return root;
    }
};

打日志版本:

class Solution {
public:
    TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) {
        if (postorder.empty()) return nullptr; //后序为空,那中序肯定也为空,只用判断一个就行

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

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

        // 找到中序遍历的切割点
        int delimiterIndex = 0; //在for循环外面还要使用,所以定义在外面
        for (; 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());

        // 以下为日志--------------------
        cout << "----------" << endl;

        cout << "leftInorder :";
        for (int i : leftInorder) {
            cout << i << " ";
        }
        cout << endl;

        cout << "rightInorder :";
        for (int i : rightInorder) {
            cout << i << " ";
        }
        cout << endl;

        cout << "leftPostorder :";
        for (int i : leftPostorder) {
            cout << i << " ";
        }
        cout << endl;

        cout << "rightPostorder :";
        for (int i : rightPostorder) {
            cout << i << " ";
        }
        cout << endl;
        //-----------------------------

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

        return root;
    }
};

6、从前序与中序遍历序列构造二叉树

class Solution {
public:
    TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {
        if (preorder.empty()) return nullptr; //后序为空,那中序肯定也为空,只用判断一个就行

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

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

        // 找到中序遍历的切割点
        int delimiterIndex = 0; //在for循环外面还要使用,所以定义在外面
        for (; 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());

        // preorder 舍弃首元素(把 中 舍弃掉)
        preorder.erase(preorder.begin());

        // 切割后序数组
        // 注意这里使用了左中序数组大小作为切割点
        // [0, leftInorder.size)
        vector<int> leftPreorder(preorder.begin(), preorder.begin() + leftInorder.size());
        // [leftInorder.size(), end)
        vector<int> rightPreorder(preorder.begin() + leftInorder.size(), preorder.end());

        root->left = buildTree(leftPreorder, leftInorder);
        root->right = buildTree(rightPreorder, rightInorder);

        return root;
    }
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
邓俊辉教授是计算机科学与技术领域著名的教育家和研究者。他在清华大学担任教授,并负责计算机算法与理论方向的研究和教学工作。邓俊辉教授是中国计算机学会副理事长、国际著名科技出版社Springer中国系列丛书主编、IEICE China Communications主编、Journal of Internet Technology编委、《数据结构算法教程》作者等。 在邓俊辉教授的指导下,他办了多次Dijkstra算法训练营,旨在培养学生对于算法学习的兴趣与能力。Dijkstra算法是一种用于图论中求解最短路径问题的经典算法,具有广泛的应用领域,如路由算法、网络规划和GPS导航系统等。在训练营中,邓俊辉教授通过讲解算法的原理和思想,引导学生进行编程实践和案例分析,帮助他们深入理解Dijkstra算法的应用场景与实际解决问题的能力。 邓俊辉教授所组织的Dijkstra算法训练营受到了广大学生的欢迎和积极参与。通过训练营的学习,学生不仅可以掌握Dijkstra算法的具体实现过程,还能了解算法设计的思路和应用的局限性。在训练营中,学生还可以与同学们进行交流和合作,共同解决实际问题,促进彼此的学术成长和人际交往能力的培养。 总之,邓俊辉的Dijkstra算法训练营为学生提供了一个系统、全面学习算法知识的平台,帮助他们培养解决实际问题的能力和思维方式。通过这样的培训,学生不仅能在学术领域取得更好的成绩,还可以为将来的职业发展打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值