代码随想录算法训练营十七天|二叉树part07

发布于:2025-07-17 ⋅ 阅读:(17) ⋅ 点赞:(0)

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

题目链接:235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode)

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。

 可以直接用之前写的二叉树的最近公共祖先的解法解决,但因为是二叉搜索树,所以可以利用一些二叉搜索树的特性。

因为是有序树,所以如果root的值介于p,q的值的中间,那么root一定是p,q的最近公共祖先;如果p,q的值都小于root的值,那么说明p,q的最近公共祖先一定在root的左子树上;同理,如果p,q的值都大于root的值,那么说明p,q的最近公共祖先一定在root的右子树上。

代码如下:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //递归法
        if((p.val<=root.val&&q.val>=root.val)||(p.val>=root.val&&q.val<=root.val))return root;
        else 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);
        return null;
    }
}

迭代法也很简单:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //迭代法
        while(root!=null){
            if((p.val<=root.val&&q.val>=root.val)||(p.val>=root.val&&q.val<=root.val))return root;
            else if(p.val<root.val&&q.val<root.val)root=root.left;
            else if(p.val>root.val&&q.val>root.val)root=root.right;
        }
        return null;
    }
}

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

题目链接:701. 二叉搜索树中的插入操作 - 力扣(LeetCode)

给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。

示例 1:


输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]
解释:另一个满足题目要求可以通过的树是:

示例 2:

输入:root = [40,20,60,10,30,50,70], val = 25
输出:[40,20,60,10,30,50,70,null,null,25]
示例 3:

输入:root = [4,2,7,1,3,null,null,null,null,null,null], val = 5
输出:[4,2,7,1,3,5]

 这里不考虑题目中所说的改变树的结构的插入方式,直接按照二叉搜索树的规则去遍历,遇到空节点插入就行了。

递归代码如下:

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        //递归
        TreeNode node=new TreeNode(val);
        if(root==null){
            root=node;
            return root;
        }
        search(root,node);
        return root;
    }
    public void search(TreeNode root,TreeNode node){
        if(root.left==null&&node.val<root.val)root.left=node;
        if(root.right==null&&node.val>root.val)root.right=node;
        if(node.val>root.val)search(root.right,node);
        else if(node.val<root.val)search(root.left,node);
    }
}

对于迭代法,需要记录当前遍历的节点的父节点,需要用到pre和Newroot两个新的节点。和之前做过的二叉搜索树的最小绝对差、二叉树的众数技巧相似。

代码如下: 

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        //迭代
        TreeNode node=new TreeNode(val);
        if(root==null)return node;
        TreeNode pre=null;
        TreeNode Newroot=root;
        while(Newroot!=null){
            pre=Newroot;
            if(Newroot.val>val)Newroot=Newroot.left;
            else if(Newroot.val<val)Newroot=Newroot.right;
        }
        if(pre.val>val)pre.left=node;
        else if(pre.val<val)pre.right=node;
        return root;
    }
}

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

题目链接:450. 删除二叉搜索树中的节点 - 力扣(LeetCode)

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。

示例 1:

输入:root = [5,3,6,2,4,null,7], key = 3
输出:[5,4,6,2,null,null,7]
解释:给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
另一个正确答案是 [5,2,6,null,4,null,7]。


示例 2:

输入: root = [5,3,6,2,4,null,7], key = 0
输出: [5,3,6,2,4,null,7]
解释: 二叉树不包含值为 0 的节点
示例 3:

输入: root = [], key = 0
输出: []

 首先要明白删除节点有几种情况:

1.没找到删除的节点,遍历到空节点直接返回;

2.找到删除的节点,该节点左右孩子都为空,说明是叶子节点,直接删除即可;

3.删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位;

4.删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位;

5.删除节点的左右孩子都不为空,则将删除节点的左子树的头节点放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

第五种情况示例动画如下:

这是因为二叉搜索树的特性,对于删除节点来说,它的右子树上的值都大于删除节点的值,左子树上的值都小于删除节点的值,所以左子树上的最大值小于右子树上的最小值,所以左子树的头结点要放到右子树最左面节点的左孩子上,这样才能保证二叉树的特性不变。

知道了这五种情况之后,递归代码就好写了:

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        //递归
        if(root==null)return null;
        if(root.val==key){
            if(root.left==null)return root.right;
            else if(root.right==null)return root.left;
            else{
                TreeNode tmp=root.right;
                while(tmp.left!=null){
                    tmp=tmp.left;
                }
                tmp.left=root.left;
                root=root.right;
                return root;
            }
        }
        else if(root.val<key)root.right=deleteNode(root.right,key);
        else root.left=deleteNode(root.left,key);
        return root;
    }
}

 迭代法依然要用到两个额外的节点,pre用来保存前序节点:

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        //迭代
        if (root == null)
            return null;
        TreeNode tmp = root;
        TreeNode pre = null;
        while (tmp != null) {
            if (tmp.val > key) {
                pre = tmp;
                tmp = tmp.left;
            } else if (tmp.val < key) {
                pre = tmp;
                tmp = tmp.right;
            } else
                break;
        }
        if (pre == null)return delete(tmp);
        if (pre.left != null && pre.left.val == key) {
            pre.left = delete(tmp);
        }
        if (pre.right != null && pre.right.val == key) {
            pre.right = delete(tmp);
        }
        return root;
    }
    public TreeNode delete(TreeNode node){
        if(node==null)return null;
        if(node.right==null)return node.left;
        TreeNode cur=node.right;
        while(cur.left!=null){
            cur=cur.left;
        }
        cur.left=node.left;
        node=node.right;
        return node;
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到