代码随想录算法训练营 day22 || 235. 二叉搜索树的最近公共祖先,701.二叉搜索树中的插入操作,450.删除二叉搜索树中的节点

视频讲解:

二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先_哔哩哔哩_bilibili

原来这么简单? | LeetCode:701.二叉搜索树中的插入操作_哔哩哔哩_bilibili

调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点_哔哩哔哩_bilibili

235. 二叉搜索树的最近公共祖先

思路:昨天的二叉树的最近公共祖先是使用路径重合位置的方法来实现的,并没有采用递归的方式进行实现,其实路径方法也可以用在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

701.二叉搜索树中的插入操作

思路: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;
    }
}

450.删除二叉搜索树中的节点

思路:这道题我认为核心就一个,用待删除节点的左子树最右或者右子树最左来替代即可解决问题。但是本体的递归思想很厉害,没想到可以这样写。其中关于定位节点位于的方向非常的有必要,然后对于定位到的节点,直接在其位置上就进行操作,因为常理上应当是定位到待删除结点的父节点,然后对父节点的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;
    }
}

你可能感兴趣的:(算法)