文章目录
day22学习内容
day22主要内容
- 二叉搜索树的最近公共祖先
- 二叉搜索树中的插入操作
- 删除二叉搜索树中的节点
声明
本文思路和文字,引用自《代码随想录》
一、二叉搜索树的最近公共祖先
1.1、思路
1.2、正确写法
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 leftNode = lowestCommonAncestor(root.left, p, q);
if (leftNode != null) {
return leftNode;
}
}
if (root.val < p.val && root.val < q.val) {
TreeNode rightNode = lowestCommonAncestor(root.right, p, q);
if (rightNode != null) {
return rightNode;
}
}
return root;
}
}
二、二叉搜索树中的插入操作
2.1、思路
模拟法思路
- 检查树是否为空
- 如果树为空,直接创建一个新节点作为树的根节点,并返回这个新节点。
- 寻找插入位置
- 使用一个循环来遍历树,直到找到合适的插入位置。遍历的过程中需要保持对当前节点的父节点的引用,因为插入操作将在父节点进行。
- 寻找要插入节点的位置:
- 如果当前节点的值大于待插入值,根据二叉搜索树的性质,要插入的节点肯定在当前节点的左边,所以要移动到左子节点。
- 如果当前节点的值小于待插入值,根据二叉搜索树的性质,要插入的节点肯定在当前节点的右边,所以要移动到右子节点。
- 这个过程会持续进行,直到当前节点为空,这意味着找到了插入的位置。
- 插入新节点
- 根据最后一次比较的结果来确定新节点是作为左子节点还是右子节点插入。如果待插入值小于父节点的值,则作为左子节点插入;如果大于父节点的值,则作为右子节点插入。
- 返回根节点
- 返回最初的根节点
2.2、正确写法-迭代
class Solution {
TreeNode newRoot;
TreeNode pre;
public TreeNode insertIntoBST(TreeNode root, int val) {
// 不存在根节点,直接new新的节点并返回
if (root == null) {
TreeNode node = new TreeNode(val);
return node;
}
newRoot = root;
pre = root;
// 找当前元素要插入的位置,直到为null就说明找到了要插入的位置
while (root != null) {
// 保留上一个节点(父节点)
pre = root;
if (root.val > val) {
root = root.left;
} else {
root = root.right;
}
}
// 根据值的大小,判断接上父节点的哪一个位置,是左孩子节点还是右孩子节点
if (pre.val > val) {
pre.left = new TreeNode(val);
} else {
pre.right = new TreeNode(val);
}
return newRoot;
}
}
三、删除二叉搜索树中的节点
3.1 思路-右子树继位
- 比较难的一题,主要是我完全没有思路吧。
- 分析删除节点的操作,一共可以分为五种情况
如果正好找到了要删除的节点
:- 情况1、没找到删除的节点,遍历到空节点直接返回了
- 情况2、左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
- 情况3、如果节点没有左子节点且右子节点不为空,我们可以直接用右子节点替换它
- 情况4、如果节点没有右子节点且左子节点不为空,我们可以直接用左子节点替换它
- 情况5、节点既有左子节点又有右子节点
- 找到右子树中最小的节点,即右子树的最左节点
- 直接将要删除节点的左子树接到找到的最小节点的左侧
- 用要删除节点的右子树替换该节点,完成删除操作
- 最后返回该节点即可。
- 递归删除
如果没有找到要删除的节点就需要递归查找节点了
。- 如果当前节点不是要删除的节点,则根据要删除的节点值与当前节点值的比较,决定是向左子树递归查找(root.left = deleteNode(root.left, key);),还是向右子树递归查找(root.right = deleteNode(root.right, key);)。
- 这样保证了在整棵树中都能正确地找到并删除目标节点。
3.2 代码-右子树继位
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null)
return root; // 如果根节点为空,直接返回null。
// 如果找到要删除的节点
if (root.val == key) {
// 情况1: 左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
if (root.left == null && root.right == null) {
return null;
// 情况2: 如果节点没有左子节点,我们可以直接用右子节点替换它
} else if (root.left == null) {
return root.right;
}
// 情况3: 如果节点没有右子节点,我们可以直接用左子节点替换它
else if (root.right == null) {
return root.left;
}
// 情况4: 节点既有左子节点又有右子节点
else {
// 找到右子树中最小的节点,即右子树的最左节点
TreeNode cur = root.right;
while (cur.left != null) {
cur = cur.left;
}
// 将要删除节点的左子树接到找到的最小节点的左侧
cur.left = root.left;
// 用要删除节点的右子树替换该节点,完成删除操作
root = root.right;
return root;
}
}
// 如果要删除的节点值小于当前节点值,向左子树递归
if (root.val > key)
root.left = deleteNode(root.left, key);
// 如果要删除的节点值大于当前节点值,向右子树递归
if (root.val < key)
root.right = deleteNode(root.right, key);
return root; // 返回处理后的树的根节点
}
}
3.2.1、为什么没看到没找到删除的节点,遍历到空节点直接返回了代码
if (root == null)
return root; // 如果根节点为空,直接返回null。
这段代码是递归逻辑的一部分,它确保了如果在查找过程中到达了树的末端(即,遍历到了一个空节点),函数会停止递归并返回null。这就隐式地处理了“没找到删除的节点”的情况
。
解释
:当你从一个非空节点开始递归调用deleteNode方法,试图找到一个特定的key来删除,你会根据二叉搜索树的性质向左或向右递归。如果在这个过程中你到达了一个空节点(root == null),这意味着你已经走到了树的末端但没有找到值为key的节点。此时,根据二叉搜索树的定义,这个key在树中不存在,所以你直接返回null,实际上表示“在这个路径上没有找到要删除的节点”。
总结
1.感想
- 删除二叉搜索树中的节点好难,总是搞不懂 root = root.right;这句话。
2.思维导图
本文思路引用自代码随想录,感谢代码随想录作者。