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


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

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

题目链接:235. 二叉搜索树的最近公共祖先

卡尔文解

视频讲解

解题思路及注意事项:

做过二叉树:公共祖先问题 (opens new window)题目的同学应该知道,利用回溯从底向上搜索,遇到一个节点的左子树里有p,右子树里有q,那么当前节点就是最近公共祖先。

那么本题是二叉搜索树,二叉搜索树是有序的,那得好好利用一下这个特点。

在有序树里,如果判断一个节点的左子树里有p,右子树里有q呢?

因为是有序树,所有 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p。

那么只要从上到下去遍历,遇到 cur节点是数值在[p, q]区间中则一定可以说明该节点cur就是q 和 p的公共祖先。 那问题来了,一定是最近公共祖先吗?

如图,我们从根节点搜索,第一次遇到 cur节点是数值在[p, q]区间中,即 节点5,此时可以说明 p 和 q 一定分别存在于 节点 5的左子树,和右子树中

在这里插入图片描述

此时节点5是不是最近公共祖先? 如果 从节点5继续向左遍历,那么将错过成为q的祖先, 如果从节点5继续向右遍历则错过成为p的祖先。

所以当我们从上向下去递归遍历,第一次遇到 cur节点是数值在[p, q]区间中,那么cur就是 p和q的最近公共祖先。

理解这一点,本题就很好解了。

而递归遍历顺序,本题就不涉及到 前中后序了(这里没有中节点的处理逻辑,遍历顺序无所谓了)。

如图所示:p为节点6,q为节点9

在这里插入图片描述

可以看出直接按照指定的方向,就可以找到节点8,为最近公共祖先,而且不需要遍历整棵树,找到结果直接返回!

代码实现:

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

class Solution {
private:
    TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) {

        if( cur == NULL ) return cur;
                                                        // 中
        if( cur->val > p->val && cur->val > q->val ) {   // 左

            TreeNode* left = traversal( cur->left, p, q );

            if( left != NULL ) {
                return left;
            }
        }

        if( cur->val < p->val && cur->val < q->val ) {   // 右

            TreeNode* right = traversal( cur->right, p, q );

            if ( right != NULL ) {

                return right;
            }
        }
        
        return cur;
    }
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        return traversal(root, p, q);
    }
};

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

题目链接:701.二叉搜索树中的插入操作

卡尔文解

视频讲解

解题思路及注意事项:

这道题目其实是一道简单题目,但是题目中的提示:有多种有效的插入方式,还可以重构二叉搜索树,一下子吓退了不少人,瞬间感觉题目复杂了很多。

其实可以不考虑题目中提示所说的改变树的结构的插入方式。

代码实现:

/**
 * 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* insertIntoBST(TreeNode* root, int val) {

        if( !root ){
            TreeNode *node = new TreeNode( val );
            return node;
        }

        if( root->val < val ){
            root->right = insertIntoBST( root->right, val );
        }

        if( root->val > val ){
            root->left = insertIntoBST( root->left, val );
        }

        return root;
    }
};

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

题目链接:450.删除二叉搜索树中的节点

卡尔文解

视频讲解

解题思路及注意事项:

递归

递归三部曲:

确定递归函数参数以及返回值
说到递归函数的返回值,在二叉树:搜索树中的插入操作 (opens new window)中通过递归返回值来加入新节点, 这里也可以通过递归返回值删除节点。
在这里插入图片描述
确定单层递归的逻辑
这里就把二叉搜索树中删除节点遇到的情况都搞清楚。

有以下五种情况:

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

代码

实现:

/**
 * 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* deleteNode(TreeNode* root, int key) {

        // 删除节点的几种类型:
        // 1. 空节点,即树里面不存在要删除的节点。
        // 2. 叶子节点。
        // 3. 左不为空,右为空。   -  示例1: 右图中的 节点 4
        // 4. 右不为空,左为空。   -  示例1: 右图中的 节点 6
        // 5. 左不为空,右不为空。 -  示例1: 左图中的 节点 3

        if( !root ) return NULL; // 1. 树里面找不到要删除的节点

        if( root->val == key ){

            if( !root->left && !root->right ) return NULL; // 2. 找到的要删除的节点是: 叶子节点
            else if( root->left && !root->right ) return root->left;  // 3. 找到的要删除的节点是:左不空,右空
            else if( !root->left && root->right ) return root->right; // 4. 找到的要删除的节点是:右不空,左空
            else if( root->left && root->right ){

                TreeNode *cur = root->right;

                while( cur->left ){
                    cur = cur->left;
                }

                cur->left = root->left;
                return root->right;
            }
        }

        if( root->val < key ){

            root->right = deleteNode( root->right, key );
        }

        if( root->val > key ){

            root->left = deleteNode( root->left, key );
        }

        return root;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值