LeetCode 235. 二叉搜索树的最近公共祖先
思路:
这道题目相比于上一道题236. 二叉树的最近公共祖先要简单不少,因为可以利用二叉搜索树的性质。虽然可以用和二叉树同样的解法,但是合理利用二叉搜索树的性质的话可以让代码简单很多。其思路就是,对于一个二叉搜素数的两个节点p、q,它们的最近公共祖先root一定符合一下性质:
root->val <= max(p->val, q->val) && root->val >= min(p->val, q->val)
也就是说它们的最近公共祖先一定是夹在p和q两者之间的。根据236. 二叉树的最近公共祖先我们可以知道,两个节点的最近公共祖先一定要满足两个节点不在同一个子树上面,也就是说不能出现p、q同时大于root或者小于root的情况(但其中一个可以等于root),这么一来只需要递归查找到符合条件的root节点即可。
代码:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
int lower = p->val < q->val?p->val:q->val;
int upper = p->val > q->val?p->val:q->val;
return dfs(root, lower, upper);
}
TreeNode* dfs(TreeNode* root, int lower, int upper)
{
if (root->val > upper)
return dfs(root->left, lower, upper);
else if (root->val < lower)
return dfs(root->right, lower, upper);
else
return root;
}
};
LeetCode 701. 二叉搜索树中的插入操作
思路:
其实是非常简单的一道题,不知道为什么是中等难度。最重要的一点就是要找到插入的位置,根据左节点小于中间节点,右节点大于中间节点的规则,可以递归写出寻找插入位置的逻辑:当插入的值小于当前节点,往左子树查找,大于当前节点,往右子树查找,直到找到一个叶子节点后,把节点插入到叶子节点的子节点即可。
代码:
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (root == nullptr)
{
root = new TreeNode(val);
return root;
}
if (root->val > val)
root->left = insertIntoBST(root->left, val);
else
root->right = insertIntoBST(root->right, val);
return root;
}
};
LeetCode 450. 删除二叉搜索树中的节点
思路:
这题可比二叉搜索树节点插入操作难多了,要考虑很多种情况。首先是递归查找的逻辑,当没有找到要删除的节点,直接返回null,如果当前节点值大于key,递归查找左子树的节点,反之则查找右子树的节点,最后也是最重要的部分则是处理找到要删除的节点的情况。
要删除的节点也分为很多种情况。
1. 节点的左右孩子都为空,直接delete当前节点然后返回空即可。
// 左右孩子都为空节点,删除当前节点,直接返回空节点
if (root->left == nullptr && root->right == nullptr)
{
delete(root);
return nullptr;
}
2&3. 左孩子不为空,右孩子为空,或者右孩子为空,左孩子不为空,直接返回不为空的孩子节点即可。
// 左孩子为空,右孩子不为空,直接返回右孩子节点
else if (root->left == nullptr && root->right != nullptr)
{
TreeNode* right = root->right;
delete(root);
return right;
}
// 右孩子为空,左孩子不为空,直接返回左孩子节点
else if (root->left != nullptr && root->right == nullptr)
{
TreeNode* left = root->left;
delete(root);
return left;
}
4. 左右孩子都不为空,这种情况最为复杂,操作方法为:把左孩子的整颗子树放右孩子最左边的左节点上。为什么这么操作呢?仔细想想中序遍历的顺序,是先遍历到左孩子整颗子树后,再遍历中间节点,再从右子树的最左边节点开始的,但是这个时候中间节点被删除了,也就是说左子树的根节点马上接的就是右子树的最左边节点,所以才要把左边子树放在右孩子最左边的左节点上。函数changeNode()就是完成这个操作的。
// 左右孩子都不为空的情况
// 把左孩子放右孩子的最左边,返回右孩子的头节点
else
{
TreeNode* left = root->left;
changeNode(root->right, left);
TreeNode* right = root->right;
delete(root);
return right;
}
完整代码:
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == nullptr)
return nullptr;
if (root->val > key)
root->left = deleteNode(root->left, key);
else if (root->val < key)
root->right = deleteNode(root->right, key);
else // root节点即为需要删除的节点
{
// 左右孩子都为空节点,删除当前节点,直接返回空节点
if (root->left == nullptr && root->right == nullptr)
{
delete(root);
return nullptr;
}
// 左孩子为空,右孩子不为空,直接返回右孩子节点
else if (root->left == nullptr && root->right != nullptr)
{
TreeNode* right = root->right;
delete(root);
return right;
}
// 右孩子为空,左孩子不为空,直接返回左孩子节点
else if (root->left != nullptr && root->right == nullptr)
{
TreeNode* left = root->left;
delete(root);
return left;
}
// 左右孩子都不为空的情况
// 把左孩子放右孩子的最左边,返回右孩子的头节点
else
{
TreeNode* left = root->left;
changeNode(root->right, left);
TreeNode* right = root->right;
delete(root);
return right;
}
}
return root;
}
// 用于把left节点放在root节点最左边孩子的位置
void changeNode(TreeNode* root, TreeNode* left)
{
while (root->left)
root = root->left;
root->left = left;
}
};