代码随想录算法训练营
Day20 代码随想录算法训练营第 20 天 | LeetCode235. 二叉搜索树的最近公共祖先 LeetCode701.二叉搜索树中的插入操作 LeetCode 450.删除二叉搜索树中的节点
目录
前言
LeetCode235. 二叉搜索树的最近公共祖先
LeetCode701.二叉搜索树中的插入操作
LeetCode 450.删除二叉搜索树中的节点
一、LeetCode235. 二叉搜索树的最近公共祖先
1.题目链接
2.思路
(1)二叉搜索树最近公共祖先的判定:
1)由于二叉搜索树中,左子树节点<中间节点<右子树节点,所以如果第一次遇到cur->val在p->val和q->val之间,那么p,q分别在cur的左右子树中,cur就是公共祖先
2)为什么cur一定是最近公共祖先:
p,q分别在cur的左右子树中,假设p在左子树,q在右子树,如果cur向左遍历则不能作为q的祖先,向右遍历则不能作为p的祖先,所以cur是最近公共祖先
(2)递归
1)终止:空节点返回
2)单层递归:
① 如果cur->val 比p->val和q->val都大,那么最近公共祖先比cur->val小,向左遍历
也就是到cur的左子树中寻找最近公共祖先,只要最近公共祖先,不要空的节点
② 如果cur->val 比p->val和q->val都小,那么最近公共祖先比cur->val大,向右遍历
也就是到cur的右子树中寻找最近公共祖先,只要最近公共祖先,不要空的节点
③ 剩下的是cur->val在p->val和q->val之间,cur一定是最近公共祖先
3.题解
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == NULL)
return root;
if (root->val > p->val && root->val > q->val) {
TreeNode* left = lowestCommonAncestor(root->left, p, q);
if (left != NULL)
return left;
} else if (root->val < p->val && root->val < q->val) {
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (right != NULL)
return right;
}
return root;
}
};
二、LeetCode 701.二叉搜索树中的插入操作
1.题目链接
2.思路
(1)由于题目要求返回任何合理的二叉树均可,所以我在这里将新增的节点加在叶子结点上
(2)递归:
1)返回值为空,参数是当前节点和要添加的节点
2)边界:如果左节点为空且新增节点值大于cur的值,那么加到左节点上;如果右节点为空且新增节点值小于cur的值,那么加到右节点上
3)单层递归:
如果cur 的值大于新增节点值,则要去左子树中安置新节点;如果cur 的值大于新增节点值,则要去左子树中安置新节点
(3)改进的方法
1)边界条件改为判断cur是否为空,并为父节点添加左/右节点**(需要记录父节点)**
2)改为返回节点指针也可以更简单
(不过个人认为原始方法更直观一些)
3.题解
原始方法
class Solution {
public:
void trace(TreeNode* cur, TreeNode* node) {
if (cur->left == NULL && cur->val > node->val) {
cur->left = node;
return;
}
if (cur->right == NULL && cur->val < node->val) {
cur->right = node;
return;
}
if (cur->val > node->val)
trace(cur->left, node);
else
trace(cur->right, node);
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
TreeNode* node = new TreeNode(val);
if (root == NULL)
return node;
trace(root, node);
return root;
}
};
改进边界条件
class Solution {
public:
TreeNode* parent;
void trace(TreeNode* cur, int val) {
if (cur == NULL) {
TreeNode* node = new TreeNode(val);
if (parent->val > val)
parent->left = node;
else
parent->right = node;
return;
}
parent = cur;
if (cur->val > val)
trace(cur->left, val);
else
trace(cur->right, val);
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (root == NULL) {
TreeNode* node = new TreeNode(val);
return node;
}
trace(root, val);
return root;
}
};
改进返回值
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);
else
root->right = insertIntoBST(root->right, val);
return root;
}
};
三、LeetCode 450.删除二叉搜索树中的节点
1.题目链接
2.思路
(1)参数和返回值:传入根节点Root(相当于当前节点)和目标key,返回当前子树根节点
好像返回节点比void简单,因为不需要记录父节点
(2)边界条件:如果遍历到空节点就返回
(3)单层递归
五种情况处理:
1)找到删除节点,并且删除节点为叶子结点:删除节点变为NULL,返回当前节点
2)找到删除节点,并且删除节点左子节点为空,右子节点不为空:删除节点由右子节点顶替
3)找到删除节点,并且删除节点左子节点不为空,右子节点为空:删除节点由左子节点顶替
4)找到删除节点,并且删除节点左子节点不为空,右子节点不为空:
这里的做法是:删除节点的左子结点作为删除节点右子树中最左面节点的左子结点,再由右子节点补位,返回右子节点
为什么:
①删除节点由右子节点或左子节点顶替均可,这里以删除节点由右子节点顶替为例,重点关注没有顶替删除节点的那个子节点
②右子树不动,右子树中最小的节点是最左面的节点(暂且叫做A),A也是比删除节点的左子结点的值大的,所以删除节点的左子节点只有成为A的左子结点,才能确保自己左边没有更大的节点
3.题解
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == NULL)
return root;
if (root->val == key) {
TreeNode* tmp=root;
if (root->left == NULL && root->right == NULL)
root = NULL;
else if (root->left == NULL && root->right != NULL)
root = root->right;
else if (root->left != NULL && root->right == NULL)
root = root->left;
else {
TreeNode* min_in_right = root->right;
while (min_in_right->left != NULL) {
min_in_right = min_in_right->left;
}
min_in_right->left = root->left;
root = root->right;
}
delete tmp;
return root;
}
if (root->val > key)
root->left = deleteNode(root->left, key);
else
root->right = deleteNode(root->right, key);
return root;
}
};
总结
今天最难的还是删除二叉搜索树节点