力扣hot100--二叉树

目录

二叉树

1. 94. 二叉树的中序遍历

2. 98. 验证二叉搜索树

3. 101. 对称二叉树

4.102. 二叉树的层序遍历

5. 104. 二叉树的最大深度

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

7. 114. 二叉树展开为链表

8. 226. 翻转二叉树

9. 236. 二叉树的最近公共祖先

10. 538. 把二叉搜索树转换为累加树

11. 543. 二叉树的直径

12. 617. 合并二叉树

13.  108. 将有序数组转换为二叉搜索树


二叉树

1. 94. 二叉树的中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

示例 1:

2b59d5b3ef29d101630fccdb3595162d.jpeg

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

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

提示:

  • 树中节点数目在范围 [0, 100] 内
  • -100 <= Node.val <= 100

// 二叉树的遍历
class Solution {
public:
    vector<int> result; // 用来存储中序遍历的结果的向量

    // 中序遍历的递归函数
    void inorder(TreeNode* root){
        if(!root) return; // 如果节点为空,直接返回

        inorder(root->left); // 递归遍历左子树
        result.push_back(root->val); // 将当前节点的值加入结果向量
        inorder(root->right); // 递归遍历右子树
    }

    // 主函数,返回中序遍历的结果
    vector<int> inorderTraversal(TreeNode* root) {
        inorder(root); // 调用中序遍历的递归函数
        return result; // 返回结果向量
    }
};

解释: 

  • 从根节点开始,先访问左子树,然后访问根节点,最后访问右子树。
  • 对于当前的二叉树:
    • 根节点为 1,先访问左子树,但是左子树为空,因此直接访问根节点 1,将 1 加入结果数组。
    • 然后访问右子树,右子树是节点 2
    • 对于节点 2,先访问左子树,即节点 3。访问节点 3,将 3 加入结果数组。
    • 然后访问根节点 2,将 2 加入结果数组。

2. 98. 验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:1cb3c38f1e644f44b681bf03caf1d9f8.jpeg

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

示例 2:a22a9b4ddeee4d5e94d92a23809318c1.jpeg

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

提示:

  • 树中节点数目范围在[1, 104] 内
  • -231 <= Node.val <= 231 - 1

// 二叉树的遍历
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;                  // 节点的值
 *     TreeNode *left;          // 指向左子树的指针
 *     TreeNode *right;         // 指向右子树的指针
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {} // 默认构造函数
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} // 带值的构造函数
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} // 带值及左右子树的构造函数
 * };
 */
class Solution {
public:
    vector<int> v; // 存储遍历结果的向量

    // 递归遍历二叉树,进行中序遍历
    bool recursive(TreeNode* root){
        if(!root) return true; // 如果当前节点为空,返回true

        // 递归遍历左子树
        recursive(root->left);
        // 将当前节点的值加入到结果向量中
        v.push_back(root->val);
        // 递归遍历右子树
        recursive(root->right);

        return true; // 返回true表示遍历成功
    }

    // 判断二叉树是否为有效的二叉搜索树
    bool isValidBST(TreeNode* root) {
        recursive(root); // 执行中序遍历,将结果存入向量v

        // 检查向量v中的值是否是严格递增的
        for(int i{}; i < v.size(); ++i){
            if(i && v[i-1] >= v[i]){ // 如果当前值不大于前一个值
                return false; // 不是有效的二叉搜索树
            }
        }
        return true; // 所有值都满足条件,返回true
    }
};

3. 101. 对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

81dc4e85eb6e47130e6e3904bcae5504.png

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

示例 2:

5bfca717fde4ed73681b674b7f8da572.png

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

提示:

  • 树中节点数目在范围 [1, 1000] 内
  • -100 <= Node.val <= 100

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x, left(left), right(right)) {}
 * };
 */
class Solution {
public:
    // 递归函数,检查两个子树是否是镜像对称的
    bool recursive(TreeNode* left, TreeNode* right) {
        // 如果两个子树都为空,则它们对称,返回true
        if (!left && !right) return true;
        
        // 如果一个子树为空,而另一个子树不为空,则不对称,返回false
        else if (left && !right) return false;
        else if (!left && right) return false;

        // 如果两个子树的根节点值不相等,则不对称,返回false
        if (left->val != right->val) return false;
        
        // 递归检查左子树的左子节点与右子树的右子节点是否对称,以及
        // 左子树的右子节点与右子树的左子节点是否对称
        bool flag = recursive(left->left, right->right);
        bool flag1 = recursive(left->right, right->left);

        // 只有当左右两侧子树都对称时,返回true;否则返回false
        return flag && flag1;
    }

    // 检查整个树是否是对称的
    bool isSymmetric(TreeNode* root) {
        // 如果根节点为空,树是对称的,返回true
        if (!root) return true;

        // 调用递归函数,检查根节点的左子树和右子树是否是镜像对称的
        return recursive(root->left, root->right);
    }
};

4.102. 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

示例 1:cada962942334421be9b2fe2c950b78a.jpeg

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

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000] 内
  • -1000 <= Node.val <= 1000

// 二叉树的遍历
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;                  // 节点的值
 *     TreeNode *left;          // 指向左子节点的指针
 *     TreeNode *right;         // 指向右子节点的指针
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {} // 默认构造函数
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} // 带值构造函数
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} // 带值和子节点的构造函数
 * };
 */
class Solution {
public:
    vector<vector<int>> result; // 存储每一层节点值的结果

    // 层次遍历二叉树
    vector<vector<int>> levelOrder(TreeNode* root) {
        if (!root) return {}; // 如果根节点为空,返回空的二维向量

        queue<TreeNode*> q; // 创建一个队列用于存储待遍历的节点
        q.push(root); // 将根节点入队

        // 当队列不为空时,继续遍历
        while (!q.empty()) {
            int n = q.size(); // 当前层的节点数量
            vector<int> v1; // 用于存储当前层的节点值

            // 遍历当前层的所有节点
            for (int i{}; i < n; ++i) {
                TreeNode* node = q.front(); // 获取队列的前端节点
                q.pop(); // 将该节点出队

                v1.emplace_back(node->val); // 将该节点的值添加到当前层结果中

                // 将左右子节点入队(如果存在)
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }

            result.push_back(v1); // 将当前层的节点值存入结果中
        }
        return result; // 返回结果
    }
};

5. 104. 二叉树的最大深度

给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

示例 1:ebc41d7ce58846d0a0cf84c36df4c7f8.jpeg

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

示例 2:

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

提示:

  • 树中节点的数量在 [0, 104] 区间内。
  • -100 <= Node.val <= 100

/**
 * 定义二叉树节点的结构。
 * struct TreeNode {
 *     int val;             // 节点的值
 *     TreeNode *left;     // 指向左子节点的指针
 *     TreeNode *right;    // 指向右子节点的指针
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {} // 默认构造函数
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} // 带值的构造函数
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} // 带值和子节点的构造函数
 * };
 */

class Solution {
public:
    // 计算二叉树的最大深度的函数
    int maxDepth(TreeNode* root) {
        // 如果当前节点为空(基例),返回 0
        if (!root) return 0;  // 更正了原代码中的 {} 为 0

        // 递归计算左子树和右子树的深度
        // 返回两个深度的最大值加上当前节点的深度(1)
        return max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};

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

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

ad7488aa0e2aa289eb93df8143939244.jpeg

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

示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]

提示:

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorder 和 inorder 均 无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列

// 树的遍历
/**
 * 定义二叉树节点的结构。
 * struct TreeNode {
 *     int val;              // 节点的值
 *     TreeNode *left;       // 指向左子节点的指针
 *     TreeNode *right;      // 指向右子节点的指针
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}  // 默认构造函数
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}  // 带值的构造函数
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}  // 带值和左右子节点的构造函数
 * };
 */

class Solution {
public:
    // 辅助函数:递归构造二叉树
    TreeNode* recursive(vector<int>& inorder, vector<int>& preorder) {
        // 递归的终止条件:如果前序遍历的数组为空,返回空节点
        if (preorder.size() == 0) return nullptr;

        // 前序遍历的第一个元素是根节点
        TreeNode* root = new TreeNode(preorder[0]);

        // 在中序遍历中找到根节点的位置,根节点左边的是左子树,右边的是右子树
        int index = 0;
        for (int i = 0; i < inorder.size(); ++i) {
            if (inorder[i] == preorder[0]) {
                index = i;  // 找到根节点在中序遍历中的位置
                break;
            }
        }

        // 根据中序遍历分割出左子树和右子树的节点范围
        vector<int> leftInorder(inorder.begin(), inorder.begin() + index);  // 左子树的中序遍历
        vector<int> rightInorder(inorder.begin() + index + 1, inorder.end());  // 右子树的中序遍历

        // 根据前序遍历分割出左子树和右子树的节点范围
        vector<int> leftPreorder(preorder.begin() + 1, preorder.begin() + 1 + leftInorder.size());  // 左子树的前序遍历
        vector<int> rightPreorder(preorder.begin() + 1 + leftInorder.size(), preorder.end());  // 右子树的前序遍历

        // 递归构建左子树和右子树
        root->left = recursive(leftInorder, leftPreorder);  // 构建左子树
        root->right = recursive(rightInorder, rightPreorder);  // 构建右子树

        return root;  // 返回当前的根节点
    }

    // 主函数:根据前序遍历和中序遍历构造二叉树
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        // 如果前序遍历或中序遍历为空,直接返回空树
        if (preorder.size() == 0 || inorder.size() == 0) return nullptr;

        // 调用辅助函数进行递归构造
        return recursive(inorder, preorder);
    }
};

解释: 

vector<int> leftInorder(inorder.begin(), inorder.begin() + index) 的范围在数学上可以表示为 [0,index)[0, \text{index})[0,index)。

class Solution {
private:
    unordered_map<int, int> inorderMap; // 存储中序遍历的值到索引的映射
    int preorderIndex = 0; // 前序遍历的当前索引

    // 递归帮助函数,用于构建树
    TreeNode* buildTreeHelper(vector<int>& preorder, int left, int right) {
        if (right < left) return nullptr; // 基本情况:没有节点可构建,返回 nullptr

        // 获取当前根节点的值并创建 TreeNode
        int rootVal = preorder[preorderIndex++];
        TreeNode* root = new TreeNode(rootVal);
        
        // 获取当前根值在中序数组中的索引
        int inorderIndex = inorderMap[rootVal];

        // 递归构建左子树和右子树
        root->left = buildTreeHelper(preorder, left, inorderIndex - 1); // 左子树范围
        root->right = buildTreeHelper(preorder, inorderIndex + 1, right); // 右子树范围
        
        return root; // 返回构建的子树
    }

public:
    // 主函数,从前序和中序遍历构建二叉树
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        preorderIndex = 0; // 重置前序遍历索引
        // 填充中序数组的值到索引的映射
        for (int i = 0; i < inorder.size(); i++) {
            inorderMap[inorder[i]] = i; // 记录每个值在中序数组中的索引
        }
        // 开始递归构建树
        return buildTreeHelper(preorder, 0, inorder.size() - 1);
    }
};

解释:

  • unordered_map 用于存储中序遍历中的值与其对应的索引,以便快速查找。
  • preorderIndex 用于追踪前序遍历中当前的索引,以获取根节点的值。
  • buildTreeHelper 是一个递归函数,用于构建树。通过指定的范围(leftright)构建子树,并返回构建的节点。
  • 基本情况:如果当前的范围没有元素(right < left),则返回 nullptr
  • 树的构建过程
    • 创建根节点。
    • 查找根节点在中序数组中的位置。
    • 递归构建左子树和右子树。

7. 114. 二叉树展开为链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
  • 展开后的单链表应该与二叉树 先序遍历 顺序相同。

示例 1:7230b6a8babe4df38ca57d6a46403a41.jpeg

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

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [0]
输出:[0]

提示:

  • 树中结点数在范围 [0, 2000] 内
  • -100 <= Node.val <= 100

class Solution {
public:
    // 主函数,负责将二叉树展平
    void flatten(TreeNode* root) {
        vector<TreeNode*> l; // 创建一个向量用于存储树节点
        preorderTraversal(root, l); // 进行前序遍历并将节点存入向量
        int n = l.size(); // 获取节点数量

        // 遍历节点列表,从第二个节点开始
        for (int i = 1; i < n; i++) {
            TreeNode *prev = l.at(i - 1); // 上一个节点
            TreeNode *curr = l.at(i); // 当前节点
            prev->left = nullptr; // 将上一个节点的左指针设为 nullptr
            prev->right = curr; // 将上一个节点的右指针指向当前节点
        }
    }

    // 辅助函数,进行前序遍历
    void preorderTraversal(TreeNode* root, vector<TreeNode*> &l) {
        if (root != NULL) { // 如果当前节点不为空
            l.push_back(root); // 将当前节点添加到向量中
            preorderTraversal(root->left, l); // 递归遍历左子树
            preorderTraversal(root->right, l); // 递归遍历右子树
        }
    }
};

 解释:

  • flatten 函数:

    • 功能: 将给定的二叉树 root 展平。
    • 步骤:
      • 创建一个 vector<TreeNode*> l 来存储树的节点。
      • 调用 preorderTraversal 函数,传入根节点和节点列表,进行前序遍历。
      • 根据前序遍历的顺序,遍历存储节点的列表 l,将每个节点的左指针设为 nullptr,右指针指向下一个节点,最终实现链表化。
  • preorderTraversal 函数:

    • 功能: 递归进行二叉树的前序遍历,将访问到的节点存入列表 l
    • 步骤:
      • 检查当前节点 root 是否为空,如果不为空,则将其添加到列表中。
      • 递归调用左子树和右子树进行遍历。

8. 226. 翻转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

示例 1:

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

示例 2:

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

示例 3:

输入:root = []
输出:[]

提示:

  • 树中节点数目范围在 [0, 100] 内
  • -100 <= Node.val <= 100

// 递归

class Solution {/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val; // 节点的值
 *     TreeNode *left; // 指向左子节点的指针
 *     TreeNode *right; // 指向右子节点的指针
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {} // 默认构造函数,初始化值为0,左右子节点为nullptr
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} // 带值的构造函数,初始化节点值
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} // 带值和左右子节点的构造函数
 * };
 */
public:
    // 辅助函数:递归翻转二叉树
    TreeNode* invert(TreeNode* root) {
        // 如果当前节点为空,直接返回空指针
        if (!root) return root;

        // 保存当前节点的左子树和右子树
        TreeNode* left = root->left;  // 记录左子树
        TreeNode* right = root->right; // 记录右子树

        // 交换左右子树
        root->right = left; // 将左子树放到右子树的位置
        root->left = right; // 将右子树放到左子树的位置

        // 递归翻转右子树
        if (root->right) invert(root->right); // 如果右子树存在,递归翻转
        // 递归翻转左子树
        if (root->left) invert(root->left); // 如果左子树存在,递归翻转

        // 返回当前节点,完成翻转
        return root;
    }

    // 主函数:调用辅助函数进行翻转
    TreeNode* invertTree(TreeNode* root) {
        return invert(root); // 返回翻转后的树的根节点
    }
};

解释: 

  • return root;: 当返回的是当前节点或树的根节点(指针)。常用于递归结构中返回操作后的节点。
  • return nullptr;: 当明确表示返回一个空指针时,使用 nullptr,这种写法更加语义化。
  • return;: 当函数不需要返回任何值时,直接用 return; 来结束函数。它只能用于 void 类型的函数。

9. 236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。

示例 1

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点5和节点1 的最近公共祖先是节点 3 。

示例 2:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点5和节点4的最近公共祖先是节点5。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:

输入:root = [1,2], p = 1, q = 2
输出:1

提示:

  • 树中节点数目在范围 [2, 105] 内。
  • -109 <= Node.val <= 109
  • 所有 Node.val 互不相同 。
  • p != q
  • p 和 q 均存在于给定的二叉树中。

// 递归
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val; // 节点的值
 *     TreeNode *left; // 左子节点指针
 *     TreeNode *right; // 右子节点指针
 *     // 构造函数,初始化节点值,左右子节点为 NULL
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

class Solution {
public:
    // 辅助递归函数,用于查找 p 和 q 的最近公共祖先
    TreeNode* recursive(TreeNode* root, TreeNode* p, TreeNode* q) {
        // 如果当前节点是 p、q 中的一个,或者当前节点为空,直接返回当前节点
        if (root == p || root == q || !root) return root;

        // 递归查找左子树中的 p 和 q
        TreeNode* left  = recursive(root->left, p, q);
        // 递归查找右子树中的 p 和 q
        TreeNode* right = recursive(root->right, p, q);

        // 如果 p 和 q 分别在当前节点的左右子树中,当前节点就是最近公共祖先
        if (left && right) return root;
        // 如果只有左子树中有 p 或 q,返回左子树的结果
        else if (left && !right) return left;
        // 如果只有右子树中有 p 或 q,返回右子树的结果
        else if (!left && right) return right;
        // 如果左右子树都没有 p 和 q,返回空指针
        else return {};
    }

    // 主函数,调用递归函数来查找最近公共祖先
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        return recursive(root, p, q); // 返回递归查找的结果
    }
};

 解释:

  • 如果 leftright 均不为空(即 left && right),则说明 pq 分别位于 root 的左右子树中,因此当前节点 root 就是 pq 的最近公共祖先。
  • 如果 left 不为空且 right 为空,说明 pq 都在左子树,返回 left
  • 如果 left 为空且 right 不为空,说明 pq 都在右子树,返回 right
  • 如果 leftright 都为空,则返回 nullptr

10. 538. 把二叉搜索树转换为累加树

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

  • 节点的左子树仅包含键 小于 节点键的节点。
  • 节点的右子树仅包含键 大于 节点键的节点。
  • 左右子树也必须是二叉搜索树。

注意:本题和 1038: . - 力扣(LeetCode) 相同

示例 1:

输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]

示例 2:

输入:root = [0,null,1]
输出:[1,null,1]

示例 3:

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

示例 4:

输入:root = [3,2,4,1]
输出:[7,9,4,10]

提示:

  • 树中的节点数介于 0 和 104 之间。
  • 每个节点的值介于 -104 和 104 之间。
  • 树中的所有值 互不相同 。
  • 给定的树为二叉搜索树。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val; // 节点的值
 *     TreeNode *left; // 指向左子树的指针
 *     TreeNode *right; // 指向右子树的指针
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {} // 默认构造函数
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} // 带值构造函数
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} // 带值及左右子树指针的构造函数
 * };
 */

class Solution {
public:
    int pre{}; // 用于存储前一个节点的累加值,初始化为 0

    // 递归函数,用于后续遍历树
    void recursive(TreeNode* root) {
        if (!root) return; // 如果节点为空,返回

        // 先遍历右子树
        recursive(root->right);
        
        // 将当前节点的值与前一个节点的累加值相加
        root->val += pre; // 当前节点的值更新为原值加上之前的累加值
        pre = root->val; // 更新累加值为当前节点的新值
        
        // 然后遍历左子树
        recursive(root->left);
    }

    // 主函数,转换二叉搜索树为累加树
    TreeNode* convertBST(TreeNode* root) {
        recursive(root); // 调用递归函数处理树
        return root; // 返回更新后的树的根节点
    }
};

解释: 

累加树的构建从树的右侧开始,累加节点值,并逐步向左处理。在这个过程中,每个节点的值被更新为该节点值加上它右侧所有节点的值的和。

  • 步骤1:从最右边的节点 8 开始,由于它右侧没有节点,它的值保持不变,8
  • 步骤2:处理节点 7,它的值变为 7 + 8 = 15
  • 步骤3:处理节点 6,它的值变为 6 + 15 = 21
  • 步骤4:处理节点 5,它的值变为 5 + 21 = 26
  • 步骤5:处理节点 4,它的值变为 4 + 26 = 30
  • 步骤6:处理节点 3,它的值变为 3 + 35 = 33
  • 步骤7:处理节点 2,它的值变为 2 + 33 = 35
  • 步骤8:处理节点 1,它的值变为 1 + 35 = 36
  • 步骤9:处理节点 0,它的值变为 0 + 36 = 36

11. 543. 二叉树的直径

给你一棵二叉树的根节点,返回该树的 直径 。

二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。

两节点之间路径的 长度 由它们之间边数表示。

示例 1:

输入:root = [1,2,3,4,5]
输出:3
解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。

示例 2:

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

提示:

  • 树中节点数目在范围 [1, 104] 内
  • -100 <= Node.val <= 100

class Solution {

    // 成员变量 ans,用于记录二叉树的最大直径

    int ans;

    // 定义一个递归函数 depth,计算二叉树的深度

    int depth(TreeNode* rt) {

        // 如果当前节点为空(递归到叶子节点的子节点),返回深度0

        if (rt == NULL) {

            return 0; // 空节点的深度为0

        }

        // 递归计算左子树的深度

        int L = depth(rt->left);

        // 递归计算右子树的深度

        int R = depth(rt->right);

        // 计算当前节点的直径(左子树深度 + 右子树深度),并更新最大直径

        ans = max(ans, L + R);

        // 返回当前节点为根的子树的深度(较大的子树深度 + 1)

        return max(L, R) + 1;

    }

public:

    // 主函数,计算二叉树的最大直径

    int diameterOfBinaryTree(TreeNode* root) {

        // 初始化 ans 为0(没有节点时直径为0)

        ans = 0;

        // 调用递归函数 depth,计算树的深度并更新 ans

        depth(root);

        // 返回最大直径的边数,即 ans

        return ans;

    }

};

 解释:

  • 类成员变量 ans:用于在递归过程中全局跟踪和更新最大直径。递归函数 depth() 每次更新这个成员变量。
  • 递归计算深度depth() 函数在递归计算每个节点左右子树的深度时,同时更新通过该节点的最大直径。
  • 返回最大直径:在 diameterOfBinaryTree() 函数中,我们返回的是 ans - 1,因为 ans 是节点数,直径以边数计,因此需要减1。

12. 617. 合并二叉树

给你两棵二叉树: root1 和 root2 。

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意: 合并过程必须从两个树的根节点开始。

示例 1:

输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]

示例 2:

输入:root1 = [1], root2 = [1,2]
输出:[2,2]

提示:

  • 两棵树中的节点数目在范围 [0, 2000] 内
  • -104 <= Node.val <= 104

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */

class Solution {
public:
    // 辅助递归函数,用于合并两个二叉树
    TreeNode* recursive(TreeNode* root1, TreeNode* root2) {
        // 如果两个节点都为空,返回空节点
        if (!root1 && !root2) return nullptr;
        // 如果第一个树的节点为空,返回第二棵树的节点
        else if (!root1 && root2) return root2;
        // 如果第二棵树的节点为空,返回第一棵树的节点
        else if (root1 && !root2) return root1;

        // 如果两个节点都不为空,合并两个节点的值
        root1->val += root2->val;
        
        // 递归合并左子树
        root1->left = recursive(root1->left, root2->left);
        // 递归合并右子树
        root1->right = recursive(root1->right, root2->right);

        // 返回合并后的当前节点
        return root1;
    }

    // 主函数,用于合并两棵二叉树
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        // 调用递归函数合并树
        return recursive(root1, root2);
    }
};

解释: 

  • 使用深度优先搜索(DFS)从根节点开始合并两棵树的每个节点,首先处理当前节点,然后递归处理其左子树和右子树。
  • 如果某个节点在某棵树中不存在(即为空),则直接使用另一棵树中对应的节点。
  • 如果两个节点都存在,则将它们的值相加,继续递归合并左右子树。

13. 108. 将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡  二叉搜索树。

示例 1:

输入:nums = [-10,-3,0,5,9]

输出:[0,-3,9,-10,null,5]

解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:

示例 2:

输入:nums = [1,3]
输出:[3,1]
解释:[1,null,3] 和 [3,1] 都是高度平衡二叉搜索树。

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 按 严格递增 顺序排列

class Solution {
public:
    // 主函数,接受一个整数向量并返回一个平衡的二叉搜索树的根节点
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        // 调用辅助函数 helper,传入整个数组的范围(0 到 nums.size() - 1)
        return helper(nums, 0, nums.size() - 1);
    }

    // 辅助递归函数,构建二叉搜索树
    TreeNode* helper(vector<int>& nums, int left, int right) {
        // 递归终止条件:如果左边界大于右边界,返回空指针
        if (left > right) {
            return nullptr;
        }

        // 选择中间位置的元素作为根节点
        int mid = (left + right) / 2;

        // 创建一个新的 TreeNode,值为中间元素
        TreeNode* root = new TreeNode(nums[mid]);

        // 递归构建左子树,处理数组的左半部分
        root->left = helper(nums, left, mid - 1);
        // 递归构建右子树,处理数组的右半部分
        root->right = helper(nums, mid + 1, right);

        // 返回当前节点,作为构建树的根节点
        return root;
    }
};

解释:

  • 高度平衡:由于每次选择中间元素作为根节点,这种分割保证了左右子树的节点数目接近,从而形成一个高度平衡的树结构。
  • 递归分治:每次递归将问题拆分为更小的子问题,最终通过递归构建出整个树结构。
  • mid=(i+j)/2; 表示在当前递归范围 [i, j] 中取中间位置的元素作为根节点的值。这个方式确保了每次递归只考虑当前范围的元素,将当前范围分为左右两部分以构造平衡的二叉搜索树。
  • 若替换为 mid=nums.size()/2,则 mid 变成了固定的 nums 数组整体长度的一半,无论递归调用的范围如何都会得到相同的 mid

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值