今日内容
- leetcode. 235 二叉搜索树的最近公共祖先
- leetcode. 701 二叉搜索树中的插入操作
- leetcode. 450 删除二叉搜索树中的节点
Leetcode. 235 二叉搜索树的最近公共祖先
一开始有点恍惚,还以为题目重复了,然后仔细一看是二叉搜索树。
如果是二叉搜索树的话,那么就可以不用像解决 236. 二叉树的最近公共祖先 - 力扣(LeetCode)一样,一个个节点遍历过来去寻找目标节点了。因为二叉搜索树是有序的,假如目标节点 p 和 q 分布在某一个中间节点的左右子树上,那么该中间节点的值肯定分布在 [p, q] 这一区间中。
并且,从根节点开始遍历的话,第一个值处于 [p, q] 区间的节点肯定就是节点 p 和 q 的最近公共祖先,如上图所示,若从节点 5 再次往其左右子树深入,发现会分别错过节点 q 和 p。
根据上述分析,写出如下代码:
class Solution {
TreeNode result; // 记录最近公共祖先
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 基本和二叉树的最近公共祖先类似,但使用搜索树的特性跳过不会被遍历的点
if (root == null){return root;}
// 中
int pValue = p.val;
int qValue = q.val;
// 比较p q节点的值大小,默认让p节点值小,q节点值大
if (pValue > qValue){
int temp = pValue;
pValue = qValue;
qValue = temp;
}
// 第一次出现的在p q节点值之间的节点就是搜索树的最近公共祖先
if (root.val >= pValue && root.val <= qValue){
result = root;
return root;
}
if (root.val < pValue){
TreeNode left = lowestCommonAncestor(root.right, p, q);
}
if (root.val > qValue){
TreeNode right = lowestCommonAncestor(root.left, p, q);
}
return result;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(h),h 为二叉树高度
然后看了代码随想录里的推荐写法,更加简洁:
class Solution {
TreeNode result;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root.val > p.val && root.val > q.val){return lowestCommonAncestor(root.left, p, q);}
if (root.val < p.val && root.val < q.val){return lowestCommonAncestor(root.right, p, q);}
return root;
}
}
Leetcode. 701 二叉搜索树中的插入操作
对二叉树的插入删除,最害怕的就是要重构其结构。
但是对于二叉搜索树来说,插入操作不一定会引起结构变化。因为二叉搜索树有序,所以我们将要插入的节点插入到符合搜索树定义的叶子节点上就好。
根据这个思维,写出代码:
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null){ // 若根节点为空,直接把节点插入为根节点
root = new TreeNode(val);
return root;
}
insertBST(root, val);
return root;
}
public void insertBST(TreeNode root, int val){
if (root.val > val){ // 中间节点值更大,往左子树探测
if (root.left == null){ // 若节点的左子树为空,直接插入到左子树上
root.left = new TreeNode(val);
return;
}
insertBST(root.left, val); // 否则就遍历下去
}
if (root.val < val){
if (root.right == null){
root.right = new TreeNode(val);
return;
}
insertBST(root.right, val);
}
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(h),h 为二叉树高度
Leetcode. 450 删除二叉搜索树中的节点
与搜索树的插入不同,删除就需要考虑重构搜索树了。
先列出可能出现的各种情况:
- 遍历完搜索树,发现没有目标节点。这种不用特殊处理,遍历完返回 根节点 就完事。
- 发现目标节点,且目标节点为叶子节点。这种直接让其父节点指向该目标节点的指针指向null即可。
- 发现目标节点,目标节点的左子树非空,右子树为空。这种情况让其父节点指向该目标节点的指针指向目标节点的左子树即可。
- 发现目标节点,目标节点的右子树非空,左子树为空。这种情况让其父节点指向该目标节点的指针指向目标节点的右子树即可。
- 发现目标节点,目标节点的左右子树均非空。这种情况需要先将目标节点的左子树挪到目标节点右子树中合适的位置,然后再让其父节点指向该目标节点的指针指向目标节点的右子树即可。
分析完这些情况后,就可以写代码了:
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 root.left;
} else if (root.right != null && root.left == null){ // 右子树非空,左子树为空
return root.right;
} else if (root.left == null && root.right == null){ // 叶子节点
return null;
} else { // 左右子树均非空
TreeNode node = root.right;
while (node.left != null){node = node.left;} // 寻找右子树中适合插入目标节点左子树的位置
node.left = root.left;
return root.right;
}
}
if (root.val > key){root.left = deleteNode(root.left, key);} // 递归函数返回的是删除过后的子树的根节点
if (root.val < key){root.right = deleteNode(root.right, key);}
return root;
}
}
- 时间复杂度:O(log n)
- 空间复杂度:O(log n)
总结
挺不错的,今天可以不用题解就能想出思路,并且思路也和题解大差不差,然后还可以根据思路写出对应代码了,继续加油吧!