C++刷题笔记(26)——leetcode236、235、701、450

这篇博客介绍了二叉搜索树的基本操作,包括最近公共祖先的查找,插入新节点以及删除节点的方法。对于最近公共祖先,通过后序遍历或根据节点值比较可以找到;插入节点时,遵循搜索二叉树的性质递归插入;删除节点则需要考虑不同情况,如节点的子树为空或不为空。这些操作都是基于二叉搜索树的特性进行的高效算法实现。

题目1:236.二叉树的最近公共祖先

在这里插入图片描述
首先要了解什么是祖先:如果节点p在节点root的左(右)子树中,或者p=root(即节点的祖先可以是自己),则root为p的祖先。
在这里插入图片描述
最近公共祖先:对于有根树的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。

Krahets大佬的题解中给了一种其它解释的最近公共祖先的定义:设节点root为节点 p、q 的某公共祖先,若其左子节点root->left 和右子节点root->right 都不是p、q 的公共祖先,则称root是 “最近的公共祖先” 。

那么根据定义就有三种情况:
1.节点p在节点root的左子树中,节点q在节点root的右子树中,那么节点root就是p和q的最近公共祖先;
2.节点p=节点root,节点q在root的左(右)子树中;
3.节点q=节点root,节点p在root的左(右)子树中;

至于题目中要求的深度尽可能大,可以通过后序遍历,从低向上遍历找到的即为最近公共祖先

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == q || root == p || root == NULL) return root;    //找到了p或q、或遇到了空节点
        TreeNode* left = lowestCommonAncestor(root->left, p, q);    //递归左子树
        TreeNode* right = lowestCommonAncestor(root->right, p, q);  //递归右子树
        if (left != NULL && right != NULL) return root;             //p,、q分别在root的异侧(左\右子树),root为最近公共祖先
        if (left == NULL && right != NULL) return right;            //p、q都不在root的左子树中,有两种情况:1.p、q其中一个在root的右子树中,此时right指向p(或q)  2.p、q都在root的右子树中,此时right指向最近公共祖先节点
        else if (left != NULL && right == NULL) return left;        //p、q都不在root的右子树中,两种情况同理
        else return NULL;                                           //left == NULL && right == NULL 左右子树都不包含p、q
    }
};

代码写的很巧妙,尤其是返回值left和right的几种情况,可以打上断点自己调试看看运行过程

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

在这里插入图片描述
提到二叉搜索树那肯定就要用它的特点:左子树的所有节点都小于当前节点,右子树的所有节点都大于当前节点。

在遍历过程中:
如果两个节点p、q都小于根节点,说明p、q在根节点的左子树上;
如果两个节点p、q都大于根节点,说明p、q在根节点的右子树上;
如果节点p大于根节点、节点q小于根节点,说明p在根节点的左子树上、q在根节点的右子树上,此时根节点就是p、q的最近公共祖先节点。

为什么某个节点使p、q位于左右子树,那么这个节点就是p、q的最近公共祖先:
可以这么理解,从根节点开始寻找p、q,比如寻找3和5,它们的路径(之前路径使一样的6->2->4)在节点4分开,节点4也就是它们最后一个相同的节点,根据定义也就是最近的公共祖先。
在这里插入图片描述

解法一:递归法

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root->val > p->val && root->val > q->val) {
            return lowestCommonAncestor(root->left, p, q);
        }
        else if (root->val < p->val && root->val < q->val) {
            return lowestCommonAncestor(root->right, p, q);
        }
        else return root;  //p->val <= root->val && root->val <= q->val 或者q->val <= root->val && root->val <= p->val
    }
};

解法二:迭代法

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        while(root) {
            if (root->val > p->val && root->val > q->val) {
                root = root->left;
            } else if (root->val < p->val && root->val < q->val) {
                root = root->right;
            } else return root;
        }
        return NULL;
    }
};

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

在这里插入图片描述
在这里插入图片描述
按照搜索二叉树的遍历特点去插入元素:
如果根节点的值小于val,则将根节点的右子树结点作为根节点继续搜索;
如果根节点的值大于val,则将根节点的左子树的结点作为根节点继续搜索;
当遇到结点为空时,此时返回一个新结点,进行插入操作。

在这里插入图片描述

解法一:递归法

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root == NULL) {
            TreeNode* node = new TreeNode(val);
            return node;
        }
        if (root->val > val) root->left = insertIntoBST(root->left, val);    //val比root值大,对右子树节点进行递归操作
        if (root->val < val) root->right = insertIntoBST(root->right, val);  //val比root值小,对左子树节点进行递归操作
        return root;                                                         //向上层返回已经完成插入操作的节点
    }
};

解法二:迭代法

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root == NULL) {
            TreeNode* node = new TreeNode(val);
            return node;
        }
        TreeNode* cur = root;  //记录当前节点
        TreeNode* pre = root;  //记录上一个节点
        while (cur != NULL) {
            pre = cur;
            if (cur->val > val) cur = cur->left;     //val比root值大
            else cur = cur->right;                   //val比root值小
        }
        TreeNode* node = new TreeNode(val);
        if (val < pre->val) pre->left = node;        //用parent节点的进行赋值
        else pre->right = node;
        return root;           
    }
};

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

在这里插入图片描述
删除节点的两个步骤,首先是找到要删除的节点,这一步和701的搜索操作时一样的,根据搜索二叉树的特点去遍历,找到要删除的节点。

在搜索二叉树中删除节点有以下几种情况:
1.遍历到空节点,说明没找到要删除的节点,直接返回;
2.如果要删除的节点左子树为空、右子树不为空,则删除节点,右子树顶替其位置;
3.如果要删除的节点左子树不为空、右子树为空,则删除节点,左子树顶替其位置;
4.如果要删除的节点左右子树都为空(即为叶子节点),直接删除节点;
5.如果要删除的节点左右子树都不为空,将要删除节点的左子树转移到其右子树的最左节点的左子树上,然后右子树顶替其位置,删除了该节点。

在这里插入图片描述

解法一:迭代法

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == NULL) return root;                                   //遍历到空节点,说明没找到要删除的节点,直接返回
        if (root->val > key) root->left = deleteNode(root->left, key);   //根节点的值大于key,搜索左子树
        if (root->val < key) root->right = deleteNode(root->right, key); //根节点的值小于key,搜索右子树
        if (root->val == key) {                                          //当前节点就是要删除的节点
            if (root->left == NULL && root->right == NULL) {             //要删除的节点左右子树都为空(即为叶子节点),直接删除节点
                delete root;                                             //释放内存
                return NULL;
            }
            else if (root->left == NULL && root->right != NULL) {        //要删除的节点左子树为空、右子树不为空
                TreeNode* node = root->right;                            //删除节点,右子树顶替其位置
                delete root;
                return node;
            }
            else if (root->left != NULL && root->right == NULL) {        //要删除的节点左子树不为空、右子树为空
                TreeNode* node = root->left;                             //删除节点,左子树顶替其位置
                delete root;
                return node;
            }
            else {                                                       //要删除的节点左右子树都不为空,将要删除节点的左子树转移到其右子树的最左节点的左子树上
                TreeNode* cur = root->right;                             //要删除的节点的右子树
                while (cur->left != NULL) {                              //找右子树最左边的节点
                    cur = cur->left;
                }
                cur->left = root->left;                                  //把要删除的节点(root)左子树放在cur的左子树的位置
                root = root->right;                                      //返回旧root的右子树作为新root
                return root;
            }
        }
        return root;
    }
};

代码优化后:

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key)
    {
        if (root == nullptr)    return nullptr;                                 //遍历到空节点
        if (root->val < key)    root->right = deleteNode(root->right, key);     //根节点的值小于key,搜索右子树
        else if (root->val > key)    root->left = deleteNode(root->left, key);  //根节点的值大于key,搜索左子树
        else                                                                    //当前节点就是要删除的节点
        {
            if (!root->left)   return root->right;                              //要删除的节点左子树为空、右子树不为空
            if (!root->right)  return root->left;                               //要删除的节点左子树不为空、右子树为空
            TreeNode* node = root->right;                                       //要删除的节点左右子树都不为空
            while (node->left) {                                                //寻找要删除节点右子树的最左节点
                node = node->left;
            }                                                  
            node->left = root->left;                                           //将要删除节点的左子树成为其右子树的最左节点的左子树
            root = root->right;                                                //要删除节点的右子顶替其位置,节点被删除
        }
        return root;
    }
};
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值