• 代码随想录 10.13 || 二叉树 LeetCode 235.二叉搜索树的最近公共祖先、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点


    二叉树的定义:

            回顾一下二叉树的定义,加固记忆。

    1. struct TreeNode {
    2. int val;
    3. TreeNode *left;
    4. TreeNode *right;
    5. TreeNode() : val(0), left(nullptr), right(nullptre) {}
    6. TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    7. TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
    8. };

    235.二叉搜索树的最近公共祖先

            与普通二叉树中的最近公共祖先不同,求二叉搜索树的最近公共祖先更为容易一些,因为二叉搜索树自带顺序性,我们可以根据节点的值寻找目标节点的位置。

            如果,当前节点的值同时大于 节点 p 和 q 的值,说明 p 和 q 在当前节点的左子树中,反之则位于右子树中。

            如果,当前节点的值大于其中一个节点,小于另一个节点,当前节点即为给定节点 p 和 q 的最近公共祖先。

    1. class Solution { // 递归法
    2. private:
    3. TreeNode* traversal(TreeNode *node, TreeNode *p, TreeNode *q) {
    4. if (node->val > p->val && node->val > q->val) return traversal(node->left, p, q);
    5. if (node->val < p->val && node->val < q->val) return traversal(node->right, p, q);
    6. return node;
    7. }
    8. public:
    9. TreeNode* lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q) {
    10. if (root == nullptr) return root;
    11. return traversal(root, p, q);
    12. }
    13. };

            在递归法中,我们递归遍历二叉树,在不满同时大于或小于条件时,直接返回当前节点,即最近公共祖先。

    1. class Solution { // 迭代法
    2. public:
    3. TreeNode* lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q) {
    4. while(root) {
    5. if (root->val > p->val && root->val > q->val) root = root->left;
    6. else if (root->val < p->val && root->val < q->val) root = root->right;
    7. else return root;
    8. }
    9. return nullptr;
    10. }
    11. };

            迭代法代码则更为简洁,使用 while 一直向下遍历,直至找到目标节点为止,如果未找到,则说明目标不存在给定二叉树中。

    701.二叉搜索树中的插入操作

            向一颗二叉搜索树中插入一个节点,众所周知,在二叉搜索树中,左子树的值一定小于根节点的值,右子树的值一定大于根节点的值,我们需要找到合适的位置插入给定节点,而不是应该随意插入,插入完成后的树,应该保持二叉搜索树的性质。

    1. class Solution { // 递归法
    2. private:
    3. TreeNode* traversal(TreeNode *node, int val) {
    4. if (node->val > val && node->left != nullptr) return traversal(node->left, val);
    5. if (node->val < val && node->right != nullptr) return traversal(node->right, val);
    6. return node;
    7. }
    8. public:
    9. TreeNode* insertIntoBST(TreeNode *root, int val) {
    10. if (root == nullptr) {
    11. TreeNode *node = new TreeNode(val);
    12. return node;
    13. }
    14. TreeNode *fnode = traversal(root, val);
    15. TreeNode *cnode = new TreeNode(val);
    16. if (fnode->val > val) fnode->left = cnode;
    17. else fnode->right = cnode;
    18. return root;
    19. }
    20. };

            在递归法中,我们仍然利用二叉搜索树的性质,寻找插入位置。如果,待插入节点的值小于当前节点,且当前节点的左子树不为空,则向左子树遍历;如果,待插入节点的值大于当前节点,且当前节点的右子树不为空,则向右子树遍历。不满足上述条件,则说明当前节点的左子树或右子树为空,此时我们就找到了插入位置的父节点,将其返回。在主函数中,我们定义一个 fnode 接收返回节点,然后以 val 初始化一个节点,并根据 val 的值,将其添加到 fnode 的左子树或右子树上。在最后,返回更新过后的 root 即可。

    1. class Solution { // 迭代法
    2. public:
    3. TreeNode* insertIntoBST(TreeNode *root, int val) {
    4. if (root == nullptr) {
    5. TreeNode *node = new TreeNode(val);
    6. return node;
    7. }
    8. TreeNode *cur = root;
    9. while(1) {
    10. if (cur->val > val && cur->left != nullptr) cur = cur->left;
    11. else if (cur->val < val && cur->right != nullptr) cur = cur->right;
    12. else break;
    13. }
    14. TreeNode *node = new TreeNode(val);
    15. if (cur->val > val) cur->left = node;
    16. else cur->right = node;
    17. return root;
    18. }
    19. };

            迭代法代码更为简洁,同样利用循环找位置,然后添加新节点至目标位置。

    450.删除二叉搜索树中的节点

            与向二叉搜索树中添加节点不同,从树中删除节点会破坏树的结构,涉及到树的重塑。分析,如果删除二叉搜索树中的某一个节点,存在以下四种情况:

            1)待删除节点为叶子节点,不需要更改树的结构,直接将待删除节点置空;

            2)待删除节点的左叶子节点不为空,右叶子节点为空,将待删除节点的左子树上移;

            3)待删除节点的左叶子节点为空,右叶子节点不为空,将待删除节点的右子树上移;

            4)如果待删除节点的左右子树都不为空,需要更改二叉搜索树的结构,以确保删除节点后,仍然保持二叉搜索树的性质。可以将待删除节点的左子树挂到右子树的最左节点下,也可将待删除节点的右子树挂到左子树的最右节点下,在此我们选择第一种方法。

    1. class Solution {
    2. public:
    3. TreeNode* deleteNode(TreeNode *root, int key) {
    4. if (root == nullptr) return root;
    5. if (root->val == key) {
    6. if (root->left == nullptr && root->right == nullptr){
    7. delete root;
    8. return nullptr;
    9. } else if (root->left == nullptr && root->right != nullptr) {
    10. TreeNode *node = root->right;
    11. delete root;
    12. return node;
    13. } else if (root->left != nullptr && root->right == nullptr) {
    14. TreeNode *node = root->left;
    15. delete root;
    16. return node;
    17. } else {
    18. TreeNode *node = root->right;
    19. while (node->left != nullptr) node = node->left;
    20. node->left = root->left;
    21. TreeNode *tmp = root;
    22. root = root->right;
    23. delete tmp;
    24. return root;
    25. }
    26. }
    27. if (root->val > key) root->left = deleteNode(root->left, key);
    28. if (root->val < key) root->right = deleteNode(root->right, key);
    29. return root;
    30. }
    31. };

            在最后返回根节点 root,如果删除发生在左子树,则用左子树接收变更后的左子树,右子树亦然。

  • 相关阅读:
    【STM32】定时器与PWM的LED控制
    论文笔记--SimCSE: Simple Contrastive Learning of Sentence Embeddings
    Kotlin原理+协程基本使用
    netty Recycler对象池
    (附源码)ssm教师工作量核算统计系统 毕业设计 162307
    正则表达式
    关于近期计划调整的通知
    CentOS7安装Xrdp以便Windows远程桌面连接
    Making Anti-Palindromes
    Operator 基础原理和概念
  • 原文地址:https://blog.csdn.net/weixin_73177736/article/details/134540656