代码随想录算法学习心得19 | 235.二叉搜索树的最近公共祖先、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点...

文章介绍了如何在二叉搜索树中找到两个节点的最近公共祖先,以及如何进行插入和删除操作。最近公共祖先利用了二叉搜索树的特性,插入操作只需遍历找到空节点,删除操作则需考虑不同情况如叶子节点、单子节点和双子节点的情况。
摘要由CSDN通过智能技术生成

一、二叉搜索树中的最近公共祖先

链接:力扣

描述:

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

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

例如,给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]


思路:

1、利用回溯从底向上搜索,遇到一个节点的左子树里有p,右子树里有q,那么当前节点就是最近公共祖先。

2、利用二叉搜索树的特性,因为是有序树,所以如果中间节点是 q 和 p 的公共祖先,那么中节点的数组 一定是在 [p, q]区间的。那么只要从上到下去遍历,遇到 cur节点是数值在[p, q]区间中,则一定可以说明该节点cur就是q 和 p的公共祖先。 那问题来了,一定是最近公共祖先吗?是的。


代码如下:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
    {
        if (!root)
        {
            return NULL;
        }
        if (root == p || root == q)
        {
            return root;
        }
        //中序遍历
        TreeNode* left = lowestCommonAncestor(root->left, p, q);//左
        if (left == NULL)
        {
            //左边没找到结点,说明一定在右边
            return lowestCommonAncestor(root->right, p, q);
        }
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if (right)
        {
            return root;
        }
        else
        {
            return left;
        }
        return NULL;
    }
};


二、 二叉搜索树中的插入操作

链接:力扣

描述:

给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。


 思路:只要遍历二叉搜索树,找到空节点插入元素就可以了,那么这道题其实就简单了。

注意return的结点就是新的结点,用上一层的结点的左或者右指针接住就行。


代码如下:

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val)
    {
        if (!root)
        {
            //找到了空结点
            TreeNode* node = new TreeNode(val);
            return node;
        }
        if (root->val > val)
        {
            root->left=insertIntoBST(root->left, val);
        }
        else if (root->val < val)
        {
            root->right = insertIntoBST(root->right, val);
        }
        return root;
    }
};

运行如下:

 补充:第一次写的代码如下:

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

出现了bug:忽略了这种情况。后面改进了


三、删除二叉搜索树中的结点

链接:力扣

描述:给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

    首先找到需要删除的节点;
    如果找到了,删除它。


思路如下:按以下情况进行分析即可

  • 第一种情况:没找到删除的节点,遍历到空节点直接返回了
  • 找到删除的节点
    • 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
    • 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
    • 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
    • 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

代码如下:

#include <iostream>
using namespace std;
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* deleteNode(TreeNode* root, int key)
    {
        if (!root)
        {//未找到key,返回到上一层的空指针
            return NULL;
        }
        if (root->val == key)
        {//找到的情况
            if (root->left == NULL && root->right == NULL)
            {
                //叶子结点的情况
                delete root;
                return NULL;
            }
            else if (root->left == NULL && root->right)
            {
                //左为空,右不为空
                return root->right;
                delete root;
            }
            else if (root->right == NULL && root->left)
            {
                //左不为空,右为空
                return root->left;
                delete root;
            }
            else if (root->right && root->left)
            {
                //左右都不为空,最复杂
                //将左子树放右子树里最小的值的左孩子上
                TreeNode* cur = root->right;
                //不断遍历,找到右子树最左边的值
                while (cur&&cur->left)
                {
                    cur = cur->left;
                }
                cur->left = root->left;
                return root->right;
            }
        }

        if (root->val > key)
        {
            //确定遍历方向
            root->left = deleteNode(root->left, key);
        }
        if (root->val < key)
        {
            root->right = deleteNode(root->right,key);
        }
        return root;
    }
};
int main()
{
    TreeNode* node3 = new TreeNode(2);
    TreeNode* node4 = new TreeNode(4);
    TreeNode* node5 = new TreeNode(7);
    TreeNode* node1 = new TreeNode(3,node3,node4);
    TreeNode* node2 = new TreeNode(6,NULL,node5);
    TreeNode* root = new TreeNode(5, node1, node2);
    Solution s;
    s.deleteNode(root, 3);
    return 0;
}

运行如下:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值