代码随想录算法训练营第17天|235. 二叉搜索树的最近公共祖先、701. 二叉搜索树中的插入操作、450. 删除二叉搜索树中的节点
LeetCode235
2025-02-16 16:47:00 星期日
题目描述:力扣235
文档讲解:代码随想录(programmercarl)235. 二叉搜索树的最近公共祖先
视频讲解:《代码随想录》算法视频公开课:二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先
代码随想录视频内容简记
这道题目因为是一棵二叉搜索树,所以有显示的遍历顺序可以直接用,所以本题的遍历顺序就不重要了,说不重要就是因为没有中间结点的处理
递归法
梳理
-
if (cur == NULL) return NULL;
终止条件 -
确定单层递归的逻辑
-
首先是向左,
if (cur->val > p && cur->val > q) left = traversal(cur->left, p, q)
,这里注意,后面还有一句if (left != NULL) return left
,其实一开始不太明白这句的作用是什么,但是一想,如果left = traversal()
了以后,就必须要进行一个返回,来让下一步的递归继续,要不然中间所有的递归就断开了 -
然后是向右,一模一样
-
最后剩余的情况就是应该直接返回
-
迭代法
梳理
-
while (cur != NULL)
向下进行遍历, -
向左遍历,
if (cur->val > p->val && cur->val > q->val) cur = cur->left
-
向右同理,
else if (cur->val < p->val && cur->val < q->val) cur = cur->right
-
最后返回
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.二叉搜索树中的插入操作
代码随想录视频内容简记
在一棵二叉搜索树中插入一个新的结点,其总会变成叶子结点
梳理
-
if (cur == NULL)
,此处的终止条件反而要进行插入操作,TreeNode* root = new TreeNode(val)
,开辟一个新的结点,之后return
-
向左遍历,
if (val < cur->val) root->left = traversal(root->left)
-
之后向右遍历同理。
但是这里有个疑问,就是上面的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.删除二叉搜索树中的节点
代码随想录视频内容简记
按照递归三部曲来分析,这道题复杂的地方在于确定递归的终止条件,这里比较复杂。另外感觉本题的遍历顺序也不那么重要,没有对中间结点的处理过程
梳理
-
确定函数的返回值和参数
-
确定递归的终止条件
-
找不到要删除的结点
-
要删除结点为叶子结点
-
要删除的结点,左为空,右不为空
-
要删除的结点,左不为空,右为空
-
要删除的结点,左右都不为空
-
-
确定单层递归的逻辑

大致代码内容
- 确定递归的终止条件,
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;
}
-
确定单层递归的逻辑,
向左,
if (key < root->val) root->left = deleteNode(root->left, key);
向右,
if (key > root->val) root->right = deleteNode(root->right, key);
LeetCode测试
注意有一些小细节
-
在定义了cur指针之后,while的循环条件一开始写的
while (cur != NULL)
这样出现的问题是就是出现了对控制真的操作,所以正确的应该是while (cur->left != NULL)
-
最后单层递归的逻辑,一开始写的是
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;
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构