LeetCode刷题笔记【16】:二叉树专题-8(二叉搜索树的最近公共祖先、二叉搜索树中的插入操作、删除二叉搜索树中的节点)

前置知识

参考前文

参考文章:
LeetCode刷题笔记【9】:二叉树专题-1(分别用递归遍历、迭代遍历、标记遍历实现前、中、后序遍历)
LeetCode刷题笔记【10】:二叉树专题-2(二叉树的层序遍历、翻转二叉树、对称二叉树)
LeetCode刷题笔记【10.5】:二叉树专题-2.5(二叉树的层序遍历 - 10道题)
LeetCode刷题笔记【11】:二叉树专题-3(二叉树的最大深度、二叉树的最小深度、完全二叉树的节点个数)
LeetCode刷题笔记【12】:二叉树专题-4(平衡二叉树、二叉树的所有路径、左叶子之和)
LeetCode刷题笔记【13】:二叉树专题-5(找树左下角的值 、路径总和、从前序与中序遍历序列构造二叉树、从中序与后序遍历序列构造二叉树)
LeetCode刷题笔记【14】:二叉树专题-6(最大二叉树 、合并二叉树 、二叉搜索树中的搜索 、验证二叉搜索树)4
LeetCode刷题笔记【15】:二叉树专题-7( 二叉搜索树的最小绝对差、二叉搜索树中的众数、二叉树的最近公共祖先)

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

题目描述

在这里插入图片描述

LeetCode链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/description/

不管二叉搜索树性质

参考昨天的最后一题

LeetCode刷题笔记【15】:二叉树专题-7( 二叉搜索树的最小绝对差、二叉搜索树中的众数、二叉树的最近公共祖先)

利用二叉搜索树性质进行优化

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root->val<p->val && root->val<q->val)
            return lowestCommonAncestor(root->right, p, q);
        else if(root->val>p->val && root->val>q->val)
            return lowestCommonAncestor(root->left, p, q);
        // else if(root->val介于p->val和q->val之间)
        return root;
    }
};

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

题目描述

在这里插入图片描述

LeetCode链接:https://leetcode.cn/problems/insert-into-a-binary-search-tree/description/

搜索到叶节点然后插入

思路: 顺着二叉搜索树一直深度搜索到叶节点, 然后用新建的节点替换原先的一个NULL节点

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        TreeNode* node = new TreeNode(val);
        if(root==nullptr)
            return node;
        if(val<root->val){
            if(root->left==nullptr){
                root->left = node;
            }else{
                insertIntoBST(root->left, val);
            }
        }else if(val>root->val){
            if(root->right==nullptr){
                root->right = node;
            }else{
                insertIntoBST(root->right, val);
            }
        }
        return root;
    }
};

利用函数返回值进行操作

以上思路没有问题, 但是以下尝试另一种解法:
以上是在过程中进行操作, 其实并没有用到返回root这个点;
并且各种操作是在叶子节点上进行的, 或许可以尝试在null节点上进行.

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if(root==nullptr){
            TreeNode* node = new TreeNode(val);
            return node;
        }
        if(val<root->val){
            root->left = insertIntoBST(root->left, val);
        }else if(val>root->val){
            root->right = insertIntoBST(root->right, val);
        }
        return root;
    }
};//但是这种方法其实不如刚才那种方法, 因为遍历的节点更多了?

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

题目描述

在这里插入图片描述

LeetCode链接:https://leetcode.cn/problems/delete-node-in-a-bst/description/

针对二叉搜索树的解法

① 先搜索到目标节点target, 然后分情况讨论
首先要注意递归过程中需要cur->left = 递归function(cur->left), cur->right = 递归function(cur->right)
从而可以递归传递结果(这个很像<701. 二叉搜索树中的插入操作>中的操作)

然后:

  1. 如果左右节点都NULL, 直接删除并返回;
  2. 如果左右节点一个为NULL, 则返回非NULL的左右节点;
  3. 如果左右节点都非NULL, 那么将cur->left嫁接到cur->right这个子树的最左最深节点下, 作为其左子树, 然后返回cur->right
class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if(root==nullptr)
            return root;
        if(root->val == key){
            if(root->left==nullptr && root->right==nullptr){// 左右都为空, 删除root, 返回null
                delete root;
                return nullptr;
            }else if(root->left==nullptr && root->right!=nullptr){// left空right非空, 返回right
                TreeNode* tmp = root->right;
                delete root;
                return tmp;
            }else if(root->left!=nullptr && root->right==nullptr){// left非空right空, 返回left
                TreeNode* tmp = root->left;
                delete root;
                return tmp;
            }else{// 左右皆非空, 将cur->left嫁接到cur->right这个子树的最左最深节点下, 作为其左子树, 然后返回cur->right
                TreeNode* tmp = root->right;
                while(tmp->left != nullptr){
                    tmp = tmp->left;
                }
                tmp->left = root->left;
                tmp = root->right;
                delete root;
                return tmp;
            }
        }
        if(root->val > key) root->left = deleteNode(root->left, key);
        if(root->val < key) root->right = deleteNode(root->right, key);
        return root;
    }
};

对各类树普世的节点删除方法

② 先搜索到target, 然后再进行以下操作
如果target->right==nullptr, 直接删除target, 返回target->left
如果target->right!=nullptr, 将target->val和其右子树的最左的叶子节点的val交换, 然后继续递归遍历
(之后遍历到这个val==key的叶子节点时就直接将其删除了)

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if(root==nullptr)
            return root;
        if(root->val == key){
            if(root->right==nullptr){
                TreeNode* tmp = root->left;
                delete root;
                return tmp;
            }else{
                TreeNode* tmp = root->right;
                while(tmp->left){
                    tmp = tmp->left;
                }
                swap(tmp->val, root->val);
            }
        }
        root->left = deleteNode(root->left, key);
        root->right = deleteNode(root->right, key);
        return root;
    }
};
// 但是这种方法在后续递归的时候无法利用二叉搜索树的性质, 来减少递归的分支, 开销会增大

总结

之前以为自己对于二叉树的操作已经很熟练了, 最近两天才发现二叉树还有这么多的难点以及新奇的操作与特性;
虽然做起来挺痛苦的, 但也说明有收获, 在进步.
加油吧~

本文参考:
二叉搜索树的最近公共祖先
二叉搜索树中的插入操作
删除二叉搜索树中的节点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值