视频讲解:
二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先_哔哩哔哩_bilibili
原来这么简单? | LeetCode:701.二叉搜索树中的插入操作_哔哩哔哩_bilibili
调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点_哔哩哔哩_bilibili
思路:昨天的二叉树的最近公共祖先是使用路径重合位置的方法来实现的,并没有采用递归的方式进行实现,其实路径方法也可以用在BST的最近公共祖先上,但是时间效率较低,所以尝试理解学习递归的思想。不管是否是公共祖先问题,BST的遍历中总是可以左右条件判断后,以return作为处理的操作;但是在普通的二叉树上,确实需要left与right结果都出来后还要再联合的审查一遍。
二叉树的最近公共祖先(递归方法)
// 时间复杂度O(n),空间复杂度O(1)
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 使用递归的方法进行解题
// 感觉很多时候二叉树的题目写递归都可以直接在原函数上进行递归,而不用额外写一个遍历函数
if(root == null)
return null;
// 找到当前的元素,也是一方为另一方祖先时,可以直接返回是祖先的那个节点
if(root == q || root == p)
return root;
// BST在根操作之后的左右操作中,就要上val之间的判断了,而且通常是去到左边的if之后就直接return,右边同样也是如此
// 但是普通的二叉树在获取左右之后,应当再对左右进行一番审核,然后再返回结果
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 开始处理
if(left == null && right == null) // 左右两边都找不到p,q,注意是p,q在左子树找不到,q,p在右子树也找不到
return null;
if(left != null && right == null) // 都在左子树上,通常有两种情况,在左子树某个子树的左右两侧,或者是左子树的某一条路径上,总之其祖先都是左子树上的一个节点
return left;
if(right != null && left == null) // 同上
return right;
return root;
}
BST的最近公共祖先
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
// 时间复杂度O(n),空间复杂度O(1)
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 其实最先跳出的解法思想,就是使用路径求交点的方法
// 但是BST上的递归是非常高效的方法,
if(root == null)
return null;
// 根操作不仅是root直接是p,q的祖先的直接返回,也是后续递归遍历过程中p,q所位于的位置进行查找
if(root == p || root == q) // 找到其中一个节点,或者两个节点位于同一条路径上时,返回的root已经是两个节点的最近公共祖先了
return root;
if(p.val < root.val && q.val < root.val)
return lowestCommonAncestor(root.left, p, q);
else if(p.val > root.val && q.val > root.val)
return lowestCommonAncestor(root.right, p, q);
else if((p.valroot.val) || (p.val>root.val && q.val
思路:BST中的节点插入并不需要对BST的整体结构进行调整,只需要在BST对于左右子树与中间节点之间的关系之下,找到第一个叶子位置,将节点放入其中即可。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
// 时间复杂度O(n),空间复杂度O(1)
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
// 解题的思路非常明确,在遍历BST的过程中找到适合的叶子节点处将新节点插入;
// 如果是需要平衡的BST,那么会在第一种放插入完成后,进行旋转形成合适的平衡BST,从而出现类似于第二种测试用例的结果
if(root == null)
return new TreeNode(val, null, null);
preOrderTraversal(root, val);
return root;
}
public void preOrderTraversal(TreeNode t, int val){
if(t == null)
return;
// 首先确定位于左子树
if(t.val > val){
// 其次在左子树上找一个叶子节点
if(t.left != null)
preOrderTraversal(t.left, val);
else{
TreeNode node = new TreeNode(val, null, null);
t.left = node;
return;
}
}
// 在右子树上
else{
// 找右子树的一个叶子节点
if(t.right != null)
preOrderTraversal(t.right, val);
else{
TreeNode node = new TreeNode(val, null, null);
t.right = node;
return;
}
}
return;
}
}
思路:这道题我认为核心就一个,用待删除节点的左子树最右或者右子树最左来替代即可解决问题。但是本体的递归思想很厉害,没想到可以这样写。其中关于定位节点位于的方向非常的有必要,然后对于定位到的节点,直接在其位置上就进行操作,因为常理上应当是定位到待删除结点的父节点,然后对父节点的left或者right进行操作,然后返回被删除的节点,但是现在直接在递归上通过返回删除位置,将删除了节点的形成的子树或者路径直接缀连到了原来的树上,真的厉害。
// 时间复杂度O(n),空间复杂度O(1)
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root == null)
return null;
// 定位节点的寻找方向
if(key > root.val)
root.right = deleteNode(root.right, key);
else if(key < root.val)
root.left = deleteNode(root.left, key);
// 找到了目标的节点
else{
// 目标节点是叶子
if(root.left == null && root.right == null)
return null;
if(root.right == null && root.left != null)
return root.left;
if(root.right != null && root.left == null)
return root.right;
else{
// 此时左右子树都在,那么找左子树最右和右子树最左都可以
TreeNode node = root.right;
while(node != null && node.left != null)
node = node.left;
node.left = root.left;
root = root.right;
}
}
return root;
}
}