代码随想录算法训练营第17天|235. 二叉搜索树的最近公共祖先、701. 二叉搜索树中的插入操作、450. 删除二叉搜索树中的节点

LeetCode235

2025-02-16 16:47:00 星期日

题目描述:力扣235
文档讲解:代码随想录(programmercarl)235. 二叉搜索树的最近公共祖先
视频讲解:《代码随想录》算法视频公开课:二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先

代码随想录视频内容简记

这道题目因为是一棵二叉搜索树,所以有显示的遍历顺序可以直接用,所以本题的遍历顺序就不重要了,说不重要就是因为没有中间结点的处理

递归法

梳理

  1. if (cur == NULL) return NULL;终止条件

  2. 确定单层递归的逻辑

    1. 首先是向左,if (cur->val > p && cur->val > q) left = traversal(cur->left, p, q),这里注意,后面还有一句if (left != NULL) return left,其实一开始不太明白这句的作用是什么,但是一想,如果left = traversal()了以后,就必须要进行一个返回,来让下一步的递归继续,要不然中间所有的递归就断开了

    2. 然后是向右,一模一样

    3. 最后剩余的情况就是应该直接返回

迭代法

梳理

  1. while (cur != NULL) 向下进行遍历,

  2. 向左遍历,if (cur->val > p->val && cur->val > q->val) cur = cur->left

  3. 向右同理,else if (cur->val < p->val && cur->val < q->val) cur = cur->right

  4. 最后返回return cur

LeetCode测试

递归法

点击查看代码
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == NULL) return NULL;
        if (root->val > p->val && root->val > q->val) {
            TreeNode* left = lowestCommonAncestor(root->left, p, q);
            if (left != NULL) return left;
        }
        if (root->val < p->val && root->val < q->val) {
            TreeNode* right = lowestCommonAncestor(root->right, p, q);
            if (right != NULL) return right;
        }
        return root;
    }
};

迭代法

点击查看代码
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        while (root != NULL) {
            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;
    }
};

LeetCode701

题目描述:力扣701
文档讲解:代码随想录(programmercarl)701. 二叉搜索树中的插入操作
视频讲解:《代码随想录》算法视频公开课:原来这么简单? | LeetCode:701.二叉搜索树中的插入操作

代码随想录视频内容简记

在一棵二叉搜索树中插入一个新的结点,其总会变成叶子结点

梳理

  1. if (cur == NULL),此处的终止条件反而要进行插入操作,TreeNode* root = new TreeNode(val),开辟一个新的结点,之后return

  2. 向左遍历,if (val < cur->val) root->left = traversal(root->left)

  3. 之后向右遍历同理。

但是这里有个疑问,就是上面的root->left = traversal(root->left),一些成下面这样子就都不对了,虽然感觉逻辑上是一致的,但是我估计一是,这样写是root是接不住后面递归的子树的,二是重新定义TreeNode* left也会有问题,所以不对。

if (val < root->val) {
	TreeNode* left = insertIntoBST(root->left, val);
	return left;
}

LeetCode测试

正确的代码如下,不长

点击查看代码
class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root == NULL) {
            TreeNode* cur = new TreeNode(val);
            return cur;
        }
		// 确定单层递归的逻辑
        if (val < root->val) root->left = insertIntoBST(root->left, val);
        if (val > root->val) root->right = insertIntoBST(root->right, val);
        return root;
    }
};

LeetCode450

题目描述:力扣450
文档讲解:代码随想录(programmercarl)450. 删除二叉搜索树中的节点
视频讲解:《代码随想录》算法视频公开课:调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点

代码随想录视频内容简记

按照递归三部曲来分析,这道题复杂的地方在于确定递归的终止条件,这里比较复杂。另外感觉本题的遍历顺序也不那么重要,没有对中间结点的处理过程

梳理

  1. 确定函数的返回值和参数

  2. 确定递归的终止条件

    1. 找不到要删除的结点

    2. 要删除结点为叶子结点

    3. 要删除的结点,左为空,右不为空

    4. 要删除的结点,左不为空,右为空

    5. 要删除的结点,左右都不为空

  3. 确定单层递归的逻辑

大致代码内容

  1. 确定递归的终止条件,if (root == NULL) return NULL; 另外一个就是

if (root->val == key) {
	if (root->left == NULL && root->right == NULL) return NULL;
	else if (root->left == NULL && root->right != NULL) return root->right;
	else if (root->left != NULL && root->right == NULL) return root->left;
	// 上面的逻辑都比较简单,最复杂的是左不为空,右不为空,
	else {
		.....
	}
}

当出现左不为空,右也不为空的时候,这里默认把右子树继位,其实左子树也一样,因为这不是一棵平衡二叉树,所以都行。既然选定了右子树,那么找到右子树的最左下结点,就可以把root的左子树安排为最左下结点的左子树


else {
	TreeNode* cur = root->right;
	while (cur->left != NULL) {
		cur = cur->left;
	}
	// 这里把root左子树安排到位
	cur->left = root->left;
	// 这里就变成了左为空,右不为空的情况,直接返回root的右子树
	return root->right;
}
  1. 确定单层递归的逻辑,

    向左,if (key < root->val) root->left = deleteNode(root->left, key);

    向右,if (key > root->val) root->right = deleteNode(root->right, key);

LeetCode测试

注意有一些小细节

  1. 在定义了cur指针之后,while的循环条件一开始写的while (cur != NULL)这样出现的问题是就是出现了对控制真的操作,所以正确的应该是while (cur->left != NULL)

  2. 最后单层递归的逻辑,一开始写的是root = deleteNode(root->left, key),这样root的左孩子就接不住了,写成root->left = deleteNode(root->left, key)才对,姑且这样理解

点击查看代码
class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == NULL) return NULL;
        if (root->val == key) {
            if (root->left == NULL && root->right == NULL) return NULL;
            else if (root->left == NULL && root->right != NULL) return root->right;
            else if (root->left != NULL && root->right == NULL) return root->left;
            // 上面的逻辑都比较简单,最复杂的是左不为空,右不为空,
            else {
                TreeNode* cur = root->right;
                while (cur->left != NULL) {
                    cur = cur->left;
                }
                // 这里把root左子树安排到位
                cur->left = root->left;
                // 这里就变成了左为空,右不为空的情况,直接返回root的右子树
                return root->right;
            }
        }
        if (key < root->val) root->left = deleteNode(root->left, key);
        if (key > root->val) root->right = deleteNode(root->right, key);
        return root;
    }
};
posted on   bnbncch  阅读(1061)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示