代码随想录Day11--二叉树02-拓展题(难)

513 找树左下角的值

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

输入: root = [2,1,3]
输出: 1

示例 2:

输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

解题思路:使用广度优先搜索(BFS)来解决这个问题,首先创建一个队列并将根节点加入到队列中。然后开始处理队列中的节点,对于每个节点,都记录下它的值(最左边节点的值),并将其子节点加入到队列中。当处理完所有的节点后,最左边的节点的值就会保存在 leftMost 变量中,最后我们返回这个值。

import java.util.*;  
  
class TreeNode {  
    int val;  
    TreeNode left;  
    TreeNode right;  
  
    TreeNode(int x) {  
        val = x;  
    }  
}  
  
public class Solution {  
    public int findBottomLeftValue(TreeNode root) {  
        Queue<TreeNode> queue = new LinkedList<>();  
        queue.offer(root);  
        int leftMost = 0;  
  
        while (!queue.isEmpty()) {  
            int size = queue.size();  
            leftMost = queue.peek().val;  
  
            for (int i = 0; i < size; i++) {  
                TreeNode node = queue.poll();  
                if (node.left != null) {  
                    queue.offer(node.left);  
                }  
                if (node.right != null) {  
                    queue.offer(node.right);  
                }  
            }  
        }  
  
        return leftMost;  
    }  
  
    public static void main(String[] args) {  
        /* Assume the root node exists and has a value of 1 */  
        TreeNode root = new TreeNode(1);  
        Solution solution = new Solution();  
        int leftMostValue = solution.findBottomLeftValue(root);  
        System.out.println("The leftmost value at the bottom is: " + leftMostValue);  
    }  
}

112 路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

叶子节点 是指没有子节点的节点。

示例 1:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:

输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:

输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。

解题思路:可以通过深度优先搜索(DFS)或广度优先搜索(BFS)来解决。以下是一个用深度优先搜索解决的示例:首先检查根节点是否为空。如果为空,则返回 false,因为空树没有路径。然后我们检查当前节点是否是叶子节点并且值是否等于目标和。如果是,我们返回 true,表示我们找到了一个满足条件的路径。否则,我们递归地调用函数来检查左右子树是否存在满足条件的路径。我们通过从目标和中减去当前节点的值来更新目标和。如果左子树或右子树存在满足条件的路径,我们返回 true。否则,我们返回 false。

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {  
        if (root == null) {  
            return false;  
        }  
          
        // 递归结束条件:如果当前节点是叶子节点并且值等于目标和,则返回 true  
        if (root.left == null && root.right == null && root.val == targetSum) {  
            return true;  
        }  
          
        // 递归调用左右子树,目标和减去当前节点值  
        return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);  
    }  
}

105 从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]

解题思路:使用递归方法来实现,首先检查给定的先序遍历和中序遍历数组是否有效。然后我们定义了一个帮助方法 buildTreeHelper,该方法接收先序遍历和中序遍历的子数组,并返回构建的子树的根节点。我们通过在先序遍历数组中查找根节点值来确定在中序遍历数组中对应的索引,从而找到左右子树在中序遍历中的范围。然后我们递归地构建左右子树,将它们连接到根节点并返回根节点。

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {  
        if (preorder == null || inorder == null || preorder.length != inorder.length) {  
            return null;  
        }  
          
        return buildTreeHelper(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);  
    }  
      
    private TreeNode buildTreeHelper(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {  
        if (preStart > preEnd || inStart > inEnd) {  
            return null;  
        }  
          
        int rootValue = preorder[preStart];  
        TreeNode root = new TreeNode(rootValue);  
          
        int rootIndexInorder = -1;  
        for (int i = inStart; i <= inEnd; i++) {  
            if (inorder[i] == rootValue) {  
                rootIndexInorder = i;  
                break;  
            }  
        }  
          
        int leftSubtreeSize = rootIndexInorder - inStart;  
          
        root.left = buildTreeHelper(preorder, preStart + 1, preStart + leftSubtreeSize, inorder, inStart, rootIndexInorder - 1);  
        root.right = buildTreeHelper(preorder, preStart + leftSubtreeSize + 1, preEnd, inorder, rootIndexInorder + 1, inEnd);  
          
        return root;  
    }  
}

106 从中序与后序遍历序列构造二叉树

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:

输入:inorder = [-1], postorder = [-1]
输出:[-1]

解题思路:解决这个问题,需要根据中序遍历(inorder)和后序遍历(postorder)数组来构建二叉树。后序遍历的最后一个元素是树的根节点,然后我们可以根据中序遍历数组找到左右子树的范围,以此构建左右子树。

        这段代码中的 buildTreeHelper 方法接收中序遍历和后序遍历的子数组,并返回构建的子树的根节点。它首先查找后序遍历数组中最后一个元素(即根节点)的值,然后在中序遍历数组中找到该值的索引,该索引将子数组分为左子树和右子树。然后递归地构建左子树和右子树,并将它们连接到根节点。当遍历的子数组为空时,返回 null

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {  
        if (inorder == null || postorder == null || inorder.length != postorder.length) {  
            return null;  
        }  
  
        return buildTreeHelper(inorder, 0, inorder.length - 1, postorder, 0, postorder.length - 1);  
    }  
  
    private TreeNode buildTreeHelper(int[] inorder, int inStart, int inEnd, int[] postorder, int postStart, int postEnd) {  
        if (inStart > inEnd || postStart > postEnd) {  
            return null;  
        }  
  
        int rootValue = postorder[postEnd];  
        TreeNode root = new TreeNode(rootValue);  
  
        int rootIndexInorder = -1;  
        for (int i = inStart; i <= inEnd; i++) {  
            if (inorder[i] == rootValue) {  
                rootIndexInorder = i;  
                break;  
            }  
        }  
  
        int leftSubtreeSize = rootIndexInorder - inStart;  
  
        root.left = buildTreeHelper(inorder, inStart, rootIndexInorder - 1, postorder, postStart, postStart + leftSubtreeSize - 1);  
        root.right = buildTreeHelper(inorder, rootIndexInorder + 1, inEnd, postorder, postStart + leftSubtreeSize, postEnd - 1);  
  
        return root;  
    }  
}

654 最大二叉树

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

  1. 创建一个根节点,其值为 nums 中的最大值。
  2. 递归地在最大值 左边 的 子数组前缀上 构建左子树。
  3. 递归地在最大值 右边 的 子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树 

示例 1:

输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释:递归调用如下所示:
- [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
    - [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
        - 空数组,无子节点。
        - [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
            - 空数组,无子节点。
            - 只有一个元素,所以子节点是一个值为 1 的节点。
    - [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
        - 只有一个元素,所以子节点是一个值为 0 的节点。
        - 空数组,无子节点。

示例 2:

输入:nums = [3,2,1]
输出:[3,null,2,null,1]

解题思路:问题简化了——输入数组非空,且所有元素都是整数。       

        先定义了一个TreeNode类来表示二叉树的节点。MaxTree类中的buildTree方法是构建最大二叉树的入口,它接收一个整数数组nums并返回构建的最大二叉树的根节点。这个方法主要通过调用递归辅助方法buildTreeHelper来实现构建最大二叉树的过程。

buildTreeHelper方法接受一个数组和两个索引作为参数,这两个索引表示要处理的子数组的范围。首先,它找到这个子数组中的最大值的索引,然后以此构建最大值对应的节点,同时递归构建左子树和右子树。

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        if (nums == null || nums.length == 0) {  
            return null;  
        }  
        return buildTreeHelper(nums, 0, nums.length - 1);  
    }
    private TreeNode buildTreeHelper(int[] nums, int left, int right) {  
        if (left > right) {  
            return null;  
        }  
  
        int maxIndex = findMaxIndex(nums, left, right);  
        TreeNode root = new TreeNode(nums[maxIndex]);  
  
        root.left = buildTreeHelper(nums, left, maxIndex - 1);  
        root.right = buildTreeHelper(nums, maxIndex + 1, right);  
  
        return root;  
    }  
  
    private int findMaxIndex(int[] nums, int left, int right) {  
        int maxIndex = left;  
        for (int i = left + 1; i <= right; i++) {  
            if (nums[i] > nums[maxIndex]) {  
                maxIndex = i;  
            }  
        }  
        return maxIndex;  
    }  
}

617 合并二叉树

给你两棵二叉树: root1 和 root2 。

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意: 合并过程必须从两个树的根节点开始。

示例 1:

输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]

示例 2:

输入:root1 = [1], root2 = [1,2]
输出:[2,2]

解题思路:可以通过递归的方式来解决。首先,我们需要判断两棵树是否为空。如果其中一棵树为空,那么我们直接返回另一棵树。然后,我们比较两棵树的根节点的值,取两者中较大的一个作为新树的根节点。对于新树的左子树和右子树,我们分别递归地合并左子树和右子树。

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {  
        if (root1 == null) {  
            return root2;  
        }  
        if (root2 == null) {  
            return root1;  
        }  
          
        TreeNode merged = new TreeNode(root1.val + root2.val);  
        merged.left = mergeTrees(root1.left, root2.left);  
        merged.right = mergeTrees(root1.right, root2.right);  
          
        return merged;  
    }  
}

        在这个实现中,如果 root1 或 root2 为空,我们直接返回非空的一个。如果两个都不为空,我们创建一个新的节点,其值为两个节点值之和,然后递归地合并两个节点的左子树和右子树。最后返回合并后的树的根节点。 

700 二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点 root 和一个整数值 val

你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。

示例 1:

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

示例 2:

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

在二叉搜索树(BST)中,我们可以通过中序遍历找到给定值的节点。因为中序遍历会按照节点值的升序输出节点的序列。因此,我们可以通过中序遍历找到第一个等于给定值的节点。这里我们使用递归方法来实现。

首先我们需要定义二叉树节点的类 TreeNode,然后实现寻找等于给定值的节点的类 BSTIterator。

代码如下:

class TreeNode {  
    int val;  
    TreeNode left;  
    TreeNode right;  
    TreeNode(int x) { val = x; }  
}  
  
class BSTIterator {  
    private TreeNode node;  
    private boolean leftBound;  
    private boolean rightBound;  
  
    public BSTIterator(TreeNode root) {  
        node = root;  
        leftBound = false;  
        rightBound = false;  
    }  
  
    public boolean hasNext() {  
        if (node == null || leftBound == true || rightBound == true) {  
            return false;  
        } else {  
            return true;  
        }  
    }  
  
    public int next() {  
        if (!hasNext()) {  
            throw new NoSuchElementException();  
        }  
        int val = node.val;  
        if (node.right != null) {  
            node = node.right;  
            while (node != null && !rightBound) {  
                node = node.left;  
            }  
            rightBound = true;  
        } else {  
            while (node != null && leftBound) {  
                node = node.left;  
            }  
            if (node != null) {  
                rightBound = true;  
            } else {  
                node = node.left;  
            }     
        }         
        return val;     
    }  
}

然后我们可以使用以下方法在给定的BST中找到值等于val的节点,并返回以该节点为根的子树。如果节点不存在,则返回null。 

实现代码如下:

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if (root == null || root.val == val) {  
            return root;  
        }   
        if (val < root.val) {  
            return searchBST(root.left, val);   
        } else {   
            return searchBST(root.right, val);   
        }   
    }
}

 

98 验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

输入:root = [2,1,3]
输出:true

示例 2:

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

解题思路:使用递归,isValidBST(TreeNode node, long minVal, long maxVal) 方法检查一个给定节点的值是否在一个给定范围内,并且它的左右子树是否是有效的二叉搜索树。初始调用时,我们将范围设置为整个可能的整数范围。

如果节点为空,那么它显然是有效的。如果节点的值在给定范围内,我们递归地检查它的左右子树。如果节点的值不在给定范围内,我们返回 false

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {  
        return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);  
    }  
  
    private boolean isValidBST(TreeNode node, long minVal, long maxVal) {  
        if (node == null) {  
            return true;  
        }  
        if (node.val <= minVal || node.val >= maxVal) {  
            return false;  
        }  
        return isValidBST(node.left, minVal, node.val) && isValidBST(node.right, node.val, maxVal);  
    }  
}

530 二叉搜索树的最小绝对差

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。

差值是一个正数,其数值等于两值之差的绝对值。

示例 1:

输入:root = [4,2,6,1,3]
输出:1

示例 2:

输入:root = [1,0,48,null,null,12,49]
输出:1

解题思路:通过中序遍历来得到二叉搜索树的所有节点值,并保存到数组中。然后,遍历这个数组并计算相邻元素之间的差值,找出其中的最小值即可。

        首先,定义一个列表 res 用于保存所有节点的值。然后,使用中序遍历的方法遍历二叉搜索树,并将遍历到的节点值添加到 res 中。最后,遍历 res 中的所有元素,计算相邻元素之间的差值,并记录其中的最小值。最终,返回这个最小差值即可。

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public int getMinimumDifference(TreeNode root) {
        List<Integer> res = new ArrayList<>();  
        inorder(root, res);  
        int n = res.size();  
        int minDiff = Integer.MAX_VALUE;  
        for (int i = 1; i < n; i++) {  
            int diff = res.get(i) - res.get(i - 1);  
            minDiff = Math.min(minDiff, diff);  
        }  
        return minDiff;  
    }
    private void inorder(TreeNode root, List<Integer> res) {  
        if (root == null) {  
            return;  
        }  
        inorder(root.left, res);  
        res.add(root.val);  
        inorder(root.right, res);  
    }    
}

501 二叉搜索树中的众数

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。

如果树中有不止一个众数,可以按 任意顺序 返回。

假定 BST 满足如下定义:

  • 结点左子树中所含节点的值 小于等于 当前节点的值
  • 结点右子树中所含节点的值 大于等于 当前节点的值
  • 左子树和右子树都是二叉搜索树

示例 1:

输入:root = [1,null,2,2]
输出:[2]

示例 2:

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

解题思路:要找出二叉搜索树(BST)中的众数,可以使用中序遍历来获取树中所有节点的值,并将其存储在一个数组中。然后,使用HashMap来统计每个元素的出现次数。最后,我们找到出现次数最多的元素,即众数。

        首先使用中序遍历获取所有节点的值,然后使用一个HashMap来统计每个元素的出现次数。接下来,它找到出现次数最多的元素(可能有多个),并将这些众数存储在一个int数组中返回。

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public int[] findMode(TreeNode root) {  
        // 中序遍历获取所有节点的值  
        List<Integer> nodesValues = new ArrayList<>();  
        inorder(root, nodesValues);  
  
        // 使用HashMap统计每个元素的出现次数  
        Map<Integer, Integer> frequencyMap = new HashMap<>();  
        for (int value : nodesValues) {  
            frequencyMap.put(value, frequencyMap.getOrDefault(value, 0) + 1);  
        }  
  
        // 找到出现次数最多的元素(可能不只一个)  
        int maxFrequency = 0;  
        List<Integer> modes = new ArrayList<>();  
        for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {  
            int frequency = entry.getValue();  
            if (frequency > maxFrequency || (frequency == maxFrequency && entry.getKey() < modes.get(0))) {  
                modes.clear();  
                modes.add(entry.getKey());  
                maxFrequency = frequency;  
            } else if (frequency == maxFrequency) {  
                modes.add(entry.getKey());  
            }  
        }  
  
        // 将众数转换为int数组  
        int[] modesArray = new int[modes.size()];  
        for (int i = 0; i < modes.size(); i++) {  
            modesArray[i] = modes.get(i);  
        }  
        return modesArray;  
    }  
  
    private void inorder(TreeNode root, List<Integer> nodesValues) {  
        if (root == null) {  
            return;  
        }  
        inorder(root.left, nodesValues);  
        nodesValues.add(root.val);  
        inorder(root.right, nodesValues);  
    }
}

236 二叉树的最近公共祖先

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

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:

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

示例 2:

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

示例 3:

输入:root = [1,2], p = 1, q = 2
输出:1

解题思路:递归,首先检查当前节点是否是空或者等于我们要查找的其中两个节点,如果是,那么它就是我们要找的最近公共祖先。然后,我们对左子树和右子树分别执行相同的操作,如果左子树和右子树都找到了节点,那么当前节点就是最近的公共祖先。如果只有左子树或者只有右子树找到了节点,那么就是找到的子树的最近公共祖先。

注意,这个解决方案是基于递归的,如果树的深度很大,可能会超出递归的深度限制。在这种情况下,你可能需要使用一个基于迭代的解决方案,或者在调用这个函数之前先对树进行压缩,以减少深度。

**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {  
        if (root == null || root == p || root == q) {  
            return root;  
        }  
        TreeNode left = lowestCommonAncestor(root.left, p, q);  
        TreeNode right = lowestCommonAncestor(root.right, p, q);  
        if (left != null && right != null) {  
            return root;  
        }  
        if (left != null) {  
            return left;  
        }  
        return right;  
    }
}

迭代法实现:使用深度优先搜索(DFS)的策略

        首先检查当前节点是否是给定的两个节点之一,如果是,那么这个节点就是最近公共祖先。然后对左子树和右子树分别执行相同的操作,如果左子树和右子树都找到了节点,那么当前节点就是最近公共祖先,否则,最近公共祖先就是在左子树或右子树中找到的节点。

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {  
        if (root == null || root == p || root == q) {  
            return root;  
        }  
        TreeNode left = lowestCommonAncestor(root.left, p, q);  
        TreeNode right = lowestCommonAncestor(root.right, p, q);  
        if (left != null && right != null) {  
            return root;  
        }  
        if (left != null) {  
            return left;  
        }  
        return right;  
    }  
}

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

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

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]

示例 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)之一。如果是,我们直接返回当前节点。然后我们对左子树和右子树分别进行相同的操作,得到left和right两个节点。如果left和right都不为空,说明当前节点(root)就是我们要找的两个节点的最近公共祖先。如果只有left或者只有right不为空,那么最近公共祖先就是left或者right。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {  
        if (root == null || root == p || root == q) {  
            return root;  
        }  
        TreeNode left = lowestCommonAncestor(root.left, p, q);  
        TreeNode right = lowestCommonAncestor(root.right, p, q);  
        if (left != null && right != null) {  
            return root;  
        }  
        if (left != null) {  
            return left;  
        }  
        return right;  
    }
}

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

给定二叉搜索树(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]

解题思路:在二叉搜索树(BST)中,我们插入新值的方式相对直接。首先,我们需要找到插入位置的正确节点。对于二叉搜索树,每个节点的值都大于其左子树中的任何节点的值,并且小于其右子树中的任何节点的值。因此,我们可以通过这个特性找到插入位置。

        首先检查根节点 root 是否为空。如果为空,我们创建一个新的 TreeNode 并返回。否则,我们比较 value 和 root 的值。如果 value 小于 root 的值,我们递归地在左子树中插入 value。否则,我们在右子树中插入 value。最后,我们返回根节点 root

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode insertIntoBST(TreeNode root, int value) {  
        if (root == null) {  
            return new TreeNode(value);  
        }  
  
        if (value < root.val) {  
            root.left = insertIntoBST(root.left, value);  
        } else {  
            root.right = insertIntoBST(root.right, value);  
        }  
  
        return root;  
    }
}

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

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

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

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

示例 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. 首先,我们需要找到要删除的节点。我们可以使用二叉搜索树的特性:对于任何节点 n,其左子树中的所有节点的值都小于 n,其右子树中的所有节点的值都大于 n。因此,要找到要删除的节点,我们可以从根节点开始,比较节点值,如果找到等于 key 的节点,就停止搜索。

  2. 找到要删除的节点后,我们需要删除它。删除节点有三种可能的情况:

    • 如果要删除的节点是叶子节点(没有子节点),我们只需要将其置为 null。
    • 如果要删除的节点只有一个子节点,我们将它的子节点提升到它的位置。
    • 如果要删除的节点有两个子节点,我们需要找到它的后继节点(在右子树中值最小的节点)或者前驱节点(在左子树中值最大的节点),用它来替换要删除的节点,然后删除后继(或前驱)节点。
/**
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {  
        if (root == null) {  
            return null;  
        }  
      
        if (key < root.val) {  
            root.left = deleteNode(root.left, key);  
        } else if (key > root.val) {  
            root.right = deleteNode(root.right, key);  
        } else {  
            if (root.left == null) {  
                return root.right;  
            } else if (root.right == null) {  
                return root.left;  
            } else {  
                TreeNode successor = findSuccessor(root.right);  
                root.val = successor.val;  
                root.right = deleteNode(root.right, successor.val);  
            }  
        }  
        return root;  
    }  
  
    private TreeNode findSuccessor(TreeNode node) {  
        while (node.left != null) {  
            node = node.left;  
        }  
        return node;  
    }
}

        这个代码中的 deleteNode 方法是递归的,它根据 key 的值和当前节点的值来决定往左子树还是右子树搜索。如果找到 key 对应的节点,就根据节点的子节点的数量来决定如何删除。findSuccessor 方法是在右子树中找到值最小的节点。 

669 修剪二叉搜索树

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

示例 1:

输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]

示例 2:

输入:root = [3,0,4,null,2,null,null,1], low = 1, high = 3
输出:[3,2,null,1]

解题思路:以下是用Java实现给定边界条件下修剪二叉搜索树(BST)的代码。主要思路是迭代遍历树,当遇到值大于边界high或者小于边界low的节点时,做相应的修剪。

        其中,TreeNode是一个内部类,表示二叉树的节点,有valleftright三个属性,分别表示节点的值、左子节点和右子节点。这段代码首先检查根节点是否为空。然后,如果当前节点的值小于low,说明需要修剪的是左子树,因此递归调用trimBST(root.right, low, high)。如果当前节点的值大于high,说明需要修剪的是右子树,因此递归调用trimBST(root.left, low, high)。最后,修剪左子树和右子树,然后返回当前节点。

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {  
        if (root == null) {  
            return null;  
        }  
      
        // 如果当前节点的值小于low,说明需要修剪的是左子树  
        if (root.val < low) {  
            return trimBST(root.right, low, high);  
        }  
      
        // 如果当前节点的值大于high,说明需要修剪的是右子树  
        if (root.val > high) {  
            return trimBST(root.left, low, high);  
        }  
      
        // 修剪左子树和右子树,然后返回当前节点  
        root.left = trimBST(root.left, low, high);  
        root.right = trimBST(root.right, low, high);  
        return root;  
    }
}

108 将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

示例 1:

输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:

示例 2:

输入:nums = [1,3]
输出:[3,1]
解释:[1,null,3] 和 [3,1] 都是高度平衡二叉搜索树。

解题思路:方法一,递归构建二叉搜索树来解决。首先,我们需要确定根节点。在数组中,第一个元素将成为二叉搜索树的根节点。然后,我们将剩余的元素分成两个子数组:左子数组和右子数组。左子数组中的元素都小于根节点,而右子数组中的元素都大于根节点。

然后,我们可以递归地构建左子树和右子树。在递归调用时,我们将左子数组和右子数组作为新的参数传递给子树。如果数组为空,我们返回 null 作为子树的根节点。

在构建完左子树和右子树后,我们将它们连接到根节点。最后,我们返回根节点作为最终的高度平衡二叉搜索树。

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {  
        return sortedArrayToBSTHelper(nums, 0, nums.length - 1);  
    }  
  
    private TreeNode sortedArrayToBSTHelper(int[] nums, int left, int right) {  
        if (left > right) {  
            return null;  
        }  
  
        int mid = left + (right - left) / 2;  
        TreeNode root = new TreeNode(nums[mid]);  
  
        root.left = sortedArrayToBSTHelper(nums, left, mid - 1);  
        root.right = sortedArrayToBSTHelper(nums, mid + 1, right);  
  
        return root;  
    } 
}

 迭代法:将一个升序排列的整数数组转换为一个高度平衡的二叉搜索树

        代码定义了一个`TreeNode`类来表示二叉树节点,其中包含一个`val`属性、`left`和`right`分别表示左子树和右子树的指针。`ArrayToBSTConverter`类包含了一个`sortedArrayToBST`方法,接受一个有序整数数组作为参数,并返回一个高度平衡的二叉搜索树。

        在`constructBST`方法中,我们使用二分法将数组分为左右两个子区间,然后确定中间元素作为根节点,并递归构建左右子树。

请注意,这个实现没有使用递归,而是使用了迭代的方式来构建二叉搜索树。

/**
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        if (nums == null || nums.length == 0) {
            return null;
        }
        
        return constructBST(nums, 0, nums.length - 1);
    }
    
    private TreeNode constructBST(int[] nums, int left, int right) {
        if (left > right) {
            return null;
        }
        
        int mid = left + (right - left) / 2;
        TreeNode node = new TreeNode(nums[mid]);
        node.left = constructBST(nums, left, mid - 1);
        node.right = constructBST(nums, mid + 1, right);
        
        return node;
    }
}

538 把二叉搜索树转换为累加树

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

  • 节点的左子树仅包含键 小于 节点键的节点。
  • 节点的右子树仅包含键 大于 节点键的节点。
  • 左右子树也必须是二叉搜索树。

注意:本题和 1038: 从二叉树到更大和树 相同

示例 1:

输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]

示例 2:

输入:root = [0,null,1]
输出:[1,null,1]

示例 3:

输入:root = [1,0,2]
输出:[3,3,2]

示例 4:

输入:root = [3,2,4,1]
输出:[7,9,4,10]

解题思路:使用了递归来对二叉搜索树进行中序遍历,并按照倒序的顺序更新节点的值。具体来说,我们先遍历右子树,然后更新当前节点的值,并将这个值加到 `sum` 变量中,最后遍历左子树。

/**
 * 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;
 *     }
 * }
 */
class Solution {
    int sum = 0;
    
    public TreeNode convertBST(TreeNode root) {
        if (root != null) {
            // 遍历右子树
            convertBST(root.right);
            
            // 更新节点值为当前节点值加上所有较大节点值的和
            sum += root.val;
            root.val = sum;
            
            // 遍历左子树
            convertBST(root.left);
        }
        return root;
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值