Leetcode算法训练日记 | day18

一、找树左下角的值

1.题目

Leetcode:第 513 题

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

输入: root = [2,1,3]
输出: 1

示例 2:

输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

2.解题思路

使用队列来实现层序遍历,每次从队列中取出所有节点,并检查每个节点的子节点,将它们加入队列中以便后续遍历。在每一层中,第一个被取出的节点(即队列中的第一个节点)将是该层的最左侧节点,因此将其值设置为结果。当遍历结束时,返回结果变量,它包含了二叉树最底层的左侧值。

3.实现代码

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
    int val; // 存储节点的值。
    TreeNode* left; // 指向该节点左子树的指针。
    TreeNode* right; // 指向该节点右子树的指针。
    // TreeNode的构造函数,用于创建一个TreeNode实例。
    // 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};


// 一、找树左下角的值(递归法)
class Solution1 {
public:
    int maxDepth = INT_MIN;// 初始化最大深度为最小整数值,用于记录遍历过程中的最大深度。
    int result; // 初始化结果变量,用于存储最底层左侧的值。

    // traversal函数用于递归遍历二叉树,并找到最底层左侧的值。
    void traversal(TreeNode* root, int depth) {
        // 如果当前节点是叶子节点(没有左右子节点),检查当前深度是否大于已知的最大深度。
        if (root->left == NULL && root->right == NULL) {
            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--;
        }
        return;// 遍历过程中返回,以便继续遍历其他分支。
    }

    // findBottomLeftValue函数是公共成员函数,用于返回二叉树最底层左侧的值。
    int findBottomLeftValue(TreeNode* root) {
        traversal(root, 0);// 调用递归遍历函数,遍历二叉树。
        return result; // 返回记录的最底层左侧的值。
    }
};


// 二、找树左下角的值(迭代法)
class Solution2 {
public:
    // findBottomLeftValue函数用于找到并返回二叉树最底层的左侧值。
    int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> que; // 创建一个队列que,用于存储待遍历的节点。
        if (root != NULL) que.push(root);  // 如果根节点不为空,将其入队。
        int result = 0; // 初始化结果变量为0。

        // 使用while循环遍历队列不为空时的所有节点。
        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;// 返回记录的最底层左侧的值。
    }
};


//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {
    return new TreeNode(value);
}

// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {
    if (values.empty()) return NULL;
    TreeNode* root = createNode(values[0]);
    queue<TreeNode*> queueNode;
    queueNode.push(root);
    int i = 1;
    while (!queueNode.empty()) {
        TreeNode* node = queueNode.front();
        queueNode.pop();
        if (i < values.size()) {
            node->left = createNode(values[i++]);
            queueNode.push(node->left);
        }
        if (i < values.size()) {
            node->right = createNode(values[i++]);
            queueNode.push(node->right);
        }
    }
    return root;
}

// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {
    for (int value : vec) {
        cout << value << " ";
    }
    cout << endl;
}

// 主函数
int main() {

    vector<int> treeValues = { 1, 2, 3, 4, 5, 6, 7 };// 定义二叉树的层序遍历结果,用于构建二叉树
    TreeNode* root = buildTree(treeValues); // 构建二叉树
    Solution1 s1;// 创建Solution类的实例
    Solution2 s2;
    int result1 = s1.findBottomLeftValue(root);// 传入二叉树的根节点
    int result2 = s2.findBottomLeftValue(root);
    cout << "二叉树的左下角的值(递归法)是: " << result1 << endl;
    cout << endl;
    cout << "二叉树的左下角的值(迭代法)是: " << result2 << endl;
    cout << endl;
    return 0;
}

二、路径总和

1.题目

Leetcode:第 112 题

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

叶子节点 是指没有子节点的节点。

示例 1:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:

输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:

输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径
2.解题思路

使用递归法和迭代法遍历二叉树节点,使用一个栈来存储每个节点及其对应的路径数值。在遍历过程中,如果遇到叶子节点且其路径数值等于目标和,返回true;否则继续遍历左右子节点。如果遍历结束仍未找到满足条件的路径,返回false。

3.实现代码
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
using namespace std;

// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
    int val; // 存储节点的值。
    TreeNode* left; // 指向该节点左子树的指针。
    TreeNode* right; // 指向该节点右子树的指针。
    // TreeNode的构造函数,用于创建一个TreeNode实例。
    // 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

// 一、二叉树的路径总和(递归法)
class Solution1 {
public:
    // traversal是一个辅助函数,用于递归遍历二叉树,检查是否存在路径和等于count的路径。
    // node是当前遍历到的节点,count是当前的路径和与目标和的差值。
    bool traversal(TreeNode* node, int count) {
        // 如果当前节点是叶子节点,并且count不等于0,说明路径和不等于目标和,返回false。
        if (node->left == NULL && node->right == NULL && count != 0) return false;
        // 如果当前节点是叶子节点,并且count等于0,说明路径和等于目标和,返回true。
        if (node->left == NULL && node->right == NULL && count == 0) return true;

        // 如果当前节点有左子节点,递归遍历左子树。
        if (node->left) {
            count -= node->left->val; // 从当前路径和中减去左子节点的值,因为左子节点的值是路径和的一部分。
            if (traversal(node->left, count)) return true;  // 如果在左子树中找到满足条件的路径,返回true。
            count += node->left->val;  // 回溯,将左子节点的值加回到路径和中。
        }
        // 如果当前节点有右子节点,递归遍历右子树。
        if (node->right) {
            count -= node->right->val; // 从当前路径和中减去右子节点的值,因为右子节点的值是路径和的一部分。
            if (traversal(node->right, count)) return true;// 如果在右子树中找到满足条件的路径,返回true。
            count += node->right->val;  // 回溯,将右子节点的值加回到路径和中。
        }
        return false;// 如果遍历完左右子树都没有找到满足条件的路径,返回false。
    }

    // hasPathSum是一个成员函数,用于判断是否存在从根节点到叶子节点的路径和等于目标和的路径。
    // root是二叉树的根节点,targetSum是目标和。
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (root == NULL) return false;// 如果根节点为空,返回false,因为不存在路径。
        // 调用辅助函数traversal,从根节点开始遍历,初始路径和为targetSum - 根节点的值。
        return traversal(root, targetSum - root->val);
    }
};

// 二、二叉树的路径总和(迭代法)
class Solution2 {
public:
    // haspathsum函数用于判断是否存在路径和等于sum的路径。
    // root是二叉树的根节点,sum是目标和。
    bool haspathsum(TreeNode* root, int sum) {
        if (root == NULL) return false;// 如果根节点为空,返回false,因为不存在路径。
        stack<pair<TreeNode*, int>> st;// 创建一个栈st,用于存储节点指针和对应的路径数值。
        st.push(pair<TreeNode*, int>(root, root->val));// 将根节点及其路径数值(根节点的值)入栈。

        // 使用while循环遍历栈不为空时的所有元素。
        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.push(pair<TreeNode*, int>(node.first->right, node.second + node.first->right->val));
            }

            // 如果当前节点有左子节点,将其及其路径数值(当前路径数值加上左子节点的值)入栈。
            if (node.first->left) {
                st.push(pair<TreeNode*, int>(node.first->left, node.second + node.first->left->val));
            }
        }
        return false; // 如果遍历完所有节点都没有找到满足条件的路径,返回false。
    }
};


//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {
    return new TreeNode(value);
}

// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {
    if (values.empty()) return NULL;
    TreeNode* root = createNode(values[0]);
    queue<TreeNode*> queueNode;
    queueNode.push(root);
    int i = 1;
    while (!queueNode.empty()) {
        TreeNode* node = queueNode.front();
        queueNode.pop();
        if (i < values.size()) {
            node->left = createNode(values[i++]);
            queueNode.push(node->left);
        }
        if (i < values.size()) {
            node->right = createNode(values[i++]);
            queueNode.push(node->right);
        }
    }
    return root;
}

// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {
    for (int value : vec) {
        cout << value << " ";
    }
    cout << endl;
}

// 主函数
int main() {

    vector<int> treeValues = { 1, 2, 3, 4, 5, 6, 7 };// 定义二叉树的层序遍历结果,用于构建二叉树
    TreeNode* root = buildTree(treeValues); // 构建二叉树
    Solution1 s1;// 创建Solution类的实例
    Solution2 s2;
    int targetSum = 11;
    int result1 = s1.hasPathSum(root, targetSum);// 传入二叉树的根节点
    int result2 = s2.haspathsum(root, targetSum);
    cout << "targetSum = " << targetSum << endl;
    cout << endl;
    cout << "判断二叉树的路径总和(递归法)结果是: " << result1 << endl;
    cout << endl;
    cout << "判断二叉树的路径总和(迭代法)结果是:" << result2 << endl;
    cout << endl;
    return 0;
}

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

1.题目

Leetcode:第 106 题

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:

输入:inorder = [-1], postorder = [-1]
输出:[-1]
2.解题思路

创建traversal函数递归地根据中序和后序遍历序列重建二叉树。首先确定根节点,然后根据根节点在中序遍历序列中的位置分割序列,得到左右子树的遍历序列。接着递归地重建左右子树,并将它们分别作为根节点的左右子节点。buildTree函数是重建二叉树的入口点,它调用traversal函数并传入中序和后序遍历序列。

3.实现代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
    int val; // 存储节点的值。
    TreeNode* left; // 指向该节点左子树的指针。
    TreeNode* right; // 指向该节点右子树的指针。
    // TreeNode的构造函数,用于创建一个TreeNode实例。
    // 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};


// 从中序与后序遍历序列构造二叉树
class Solution {
private:
    // traversal函数是一个辅助函数,用于递归地重建二叉树。
    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;
        }

        // 根据根节点的位置分割中序遍历序列为左右子树的中序遍历序列。
        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());

        postorder.resize(postorder.size() - 1);// 移除后序遍历序列中的根节点,为后续递归调用做准备。

        // 根据根节点的位置分割后序遍历序列为左右子树的后序遍历序列。
        vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());

        root->left = traversal(leftInorder, leftPostorder);// 递归地重建左子树,并将其作为根节点的左子节点。
        root->right = traversal(rightInorder, rightPostorder); // 递归地重建右子树,并将其作为根节点的右子节点。

        return root;// 返回重建的根节点。
    }
public:
    // buildTree函数是公共成员函数,用于根据给定的中序和后序遍历序列重建二叉树。
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.size() == 0 || postorder.size() == 0) return NULL; // 如果中序或后序遍历序列为空,返回空指针。
        return traversal(inorder, postorder);// 调用辅助函数开始重建二叉树。
    }
};

//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {
    return new TreeNode(value);
}

// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {
    if (values.empty()) return NULL;
    TreeNode* root = createNode(values[0]);
    queue<TreeNode*> queueNode;
    queueNode.push(root);
    int i = 1;
    while (!queueNode.empty()) {
        TreeNode* node = queueNode.front();
        queueNode.pop();
        if (i < values.size()) {
            node->left = createNode(values[i++]);
            queueNode.push(node->left);
        }
        if (i < values.size()) {
            node->right = createNode(values[i++]);
            queueNode.push(node->right);
        }
    }
    return root;
}

// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {
    for (int value : vec) {
        cout << value << " ";
    }
    cout << endl;
}
int main()
{
    Solution s;
    vector<int> inorder = { 9,3,15,20,7 };
    vector<int> postorder = { 9,15,7,20,3 };
    TreeNode* result=s.buildTree(inorder, postorder);
    cout << "从中序与后序遍历序列构造二叉树:" << endl;
    cout <<"root = "<<result->val << endl;
    cout << "root->left = " << result->left->val << endl;
    cout << "root->right = " << result->right->val << endl;
    cout << "root->right->left = " << result->right->left->val << endl;
    cout << "root->right->right = " << result->right->right->val << endl;
    return 0;
}

 ps:以上皆是本人在探索算法旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值