算法-二叉树

1. 二叉树的递归遍历(LeetCode 144, 145, 94)

递归的进行先序、后序、中序遍历

/**
 * 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 List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        preorder(root, result);
        return result;
    }

    public void preorder(TreeNode root, List<Integer> result) {
        if(root == null)
            return;
        result.add(root.val);
        preorder(root.left, result);
        preorder(root.right, result);
        return;
    }
}

// 后序
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        postorder(root, result);
        return result;
    }

    public void postorder(TreeNode root, List<Integer> result){
        if(root == null)
            return;
        postorder(root.left, result);
        postorder(root.right, result);
        result.add(root.val);
    }
}

// 中序
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        inorder(root, result);
        return result;
    }

    public void inorder(TreeNode root, List<Integer> result){
        if(root == null)
            return;
        inorder(root.left, result);
        result.add(root.val);
        inorder(root.right, result);
        return ;
    }
}

2. 二叉树的非递归遍历(LeetCode 94, 145, 144)

2.1 前序遍历

class Solution {
    // 先序非递归遍历 根-左-右
    // 压栈的时候要让右节点先进入栈
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        if(root != null)
            stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if(node.right != null)
                stack.push(node.right);
            if(node.left != null)
                stack.push(node.left);
        }
        return result;
    }
}

2.2 后序遍历

在前序遍历的基础上修改

  • 前序:根左右
  • 后序:根左右(翻转左右) -> 根右左(翻转result) -> 左右根
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<>();

        if(root != null)
            stack.push(root);
        while(!stack.empty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if(node.left != null)
                stack.push(node.left);
            if(node.right != null)
                stack.push(node.right);
        }
        reverse(result);
        return result;
    }

    // 翻转数组
    // 注意List的处理方式和数组不同,需要通过length(), get(index), set(index, value)等方法
    public void reverse(List<Integer> result){
        int len = result.size();
        for(int i = 0; i<len/2 ; ++i){
            int temp = result.get(i);
            result.set(i, result.get(len-1-i));
            result.set(len-1-i, temp);
        }
    }
}

2.3 中序遍历(有难度)

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<>();

        TreeNode cur = root;
        while(cur != null || !stack.empty()){
            // 一直走到树的最底层
            if(cur != null){
                stack.push(cur);
                cur = cur.left;
            } else {
                cur = stack.pop();
                result.add(cur.val);
                cur = cur.right;
            } 
        }
        return result;
    }
}

3. 二叉树的层序遍历(LeetCode 102, 107, 199, 637, 429, 515, 116, 117, 104, 111)

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

因为需要逐层输出二叉树的节点,所以在访问每层的第一个节点前,需要记录该层的节点数量(等于此时队列的长度)levelNodeNum
因为访问完上一层的最后一个节点时,队列中保存的恰是下一层的所有节点。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if(root != null)
            queue.offer(root);
        
        while(!queue.isEmpty()){
            // 记录每层节点的数量
            int levelNodeNum = queue.size();
            // 记录每层的节点值
            List<Integer> levelResult = new ArrayList<Integer>();
            while(levelNodeNum > 0){
                TreeNode node = queue.poll();
                levelNodeNum--;     // 该层还需要遍历的节点数量减一
                levelResult.add(node.val);
                
                if(node.left != null)
                    queue.offer(node.left);
                if(node.right != null)
                    queue.offer(node.right);

            }
            // 结束一层遍历的收尾操作
            result.add(levelResult);
        }
        return result;
    }
}

4. 对称二叉树(LeetCode 101)

给你一个二叉树的根节点 root , 检查它是否轴对称。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null)
            return true;
        return compareNode(root.left, root.right);
    }

    public boolean compareNode(TreeNode left, TreeNode right) {
        if(left == null && right == null)
            return true;
        if(left == null && right != null)
            return false;
        if(left != null && right == null)
            return false;
        if(left.val != right.val)
            return false;
        // 外侧和外侧比,内侧和内侧比
        boolean outside = compareNode(left.left, right.right);
        boolean inside = compareNode(left.right, right.left);
        return outside && inside;
    }
}

5. 完全二叉树的节点个数(LeetCode 222)

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。
若最底层为第 h 层,则该层包含 1~ 2h 个节点。

  • 性质:在完全二叉树的前提下:满二叉树的最左深度和最右深度是相等的。
  • Math.pow(a, b)的返回值是double类型,可能会出现未知的问题
  • 如果要计算2的幂次,建议使用移位的方式2 << n
class Solution {
    // 时间复杂度(lgn * lgn)
    public int countNodes(TreeNode root) {
        if(root == null)
            return 0;

        int leftDepth = 0;
        int rightDepth = 0;
        TreeNode leftTemp = root.left;
        TreeNode rightTemp = root.right;
        while(leftTemp != null){
            leftDepth++;
            leftTemp = leftTemp.left;
        }
        while(rightTemp != null){
            rightDepth++;
            rightTemp = rightTemp.right;
        }

        // 如果左右子树的外侧深度相同,则以该节点为根的完全二叉树是满二叉树
        if(leftDepth == rightDepth){
            return (2 << leftDepth) - 1;
        }

        return countNodes(root.left) + countNodes(root.right) + 1;
    }
}

6. 平衡二叉树(LeetCode 110) (有难度)

给定一个二叉树,判断它是否是平衡二叉树(所有节点的左右子树的深度相差不超过1)

  • 每次计算左右节点的高度时都需要进行一次检查;如果绝对差值的大于1,则递归返回。
class Solution {
    public boolean isBalanced(TreeNode root) {
        return (getHeight(root) != -1);
    }

    // 如果不是平衡二叉树,则返回-1
    // 一旦发现有非平衡的子树,则开始递归返回。
    public int getHeight(TreeNode root) {
        if(root == null)
            return 0;
        int leftHeight = getHeight(root.left);
        if(leftHeight == -1){
            return -1;
        }
        int rightHeight = getHeight(root.right);
        if(rightHeight == -1){
            return -1;
        }
        if(Math.abs(leftHeight - rightHeight) > 1) {
            return -1;
        }
        return Math.max(leftHeight, rightHeight) + 1;
    }
}

7. 二叉树的所有路径(LeetCode 257)

给你一个二叉树的根节点 root ,按任意顺序,返回所有从根节点到叶子节点的路径。
示例

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]
class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> result = new ArrayList<String>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        preorderTraverse(root, stack, result);
        return result;   
    }

    public void preorderTraverse(TreeNode root, Stack<TreeNode> stack, List<String> result){
        if(root == null)
            return ;
        stack.push(root);
        // 访问到叶子节点,将栈中的节点打印出来
        if(root.left == null && root.right == null) {
            String path = new String();
            for(int i = 0; i < stack.size(); ++i){
                path += new String(Integer.toString(stack.get(i).val));
                if(stack.size() == 1 || i == stack.size() - 1)  {}
                else path += "->";
            }
            stack.pop();
            result.add(path);
            return ;
        }
        preorderTraverse(root.left, stack, result);
        preorderTraverse(root.right, stack, result);
        // 左右孩子都访问完了,需要弹出该节点
        stack.pop();
        return ;
    }
}

8. 左叶子之和(LeetCode 404)

给定二叉树的根节点 root ,返回所有左叶子之和。
关键是如何判定左叶子。

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if(root == null)
            return 0;

        int leftValue = sumOfLeftLeaves(root.left);
        // 左子树是叶子
        if(root.left != null && root.left.left == null && root.left.right == null)
            leftValue = root.left.val;

        int rightValue = sumOfLeftLeaves(root.right);
    
        return leftValue + rightValue;
    }
}

9. 找树左下角的值(LeetCode 513)

给定一个二叉树的 根节点 root,请找出该二叉树的最底层、最左边节点的值。
假设二叉树中至少有一个节点。

class Solution {
    // 层序遍历
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        int result = root.val;
        int levelNodeNum = 0;
        
        queue.offer(root);

        while(!queue.isEmpty()){
            levelNodeNum = queue.size();
            for(int i = 0; i<levelNodeNum; ++i){
                if(i == 0)  
                    result = queue.peek().val;
                TreeNode temp = queue.poll();
                if(temp.left != null)
                    queue.offer(temp.left);
                if(temp.right != null)
                    queue.offer(temp.right);
            }
        }
        return result;
    }
}

10. 路径总和(LeetCode 112)

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

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null)
            return false;
        
        if(root.left == null && root.right == null)
            return (targetSum == root.val);
        
        boolean leftFlag = hasPathSum(root.left, targetSum - root.val);
        boolean rightFlag = hasPathSum(root.right, targetSum - root.val);
        return (leftFlag || rightFlag);

    }
}

11. 路径总和 II(LeetCode 113)

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
注意

  • 添加到result中的应为curPath的复制体,否则已添加的result元素会随着curPath的改变而改变!
class Solution {

    List<List<Integer>> result = new ArrayList<List<Integer>>();
    LinkedList<Integer> curPath = new LinkedList<Integer>();

    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        visit(root, targetSum);
        return result;
    }

    public void visit(TreeNode root, int targetSum){
        if(root == null)
            return ;
        
        curPath.offer(root.val);

        if(root.left == null && root.right == null){
            if(root.val == targetSum) {
                // 应该复制一份添加到result中
                // 否则result中的元素会被篡改
                result.add(new LinkedList<>(curPath));
            }
            curPath.pollLast();
            return ;
        }

        visit(root.left, targetSum - root.val);
        visit(root.right, targetSum - root.val);
        curPath.pollLast();
        return ;
    }
}

12. 从中序与后序遍历序列构造二叉树(LeetCode 106) (有难度)

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

关键

  1. 对同一棵子树,后序序列和中序序列的长度是一样的。
  2. 数组的边界处理要仔细
class Solution {
    // 1. 获取后续遍历的最后一个元素
    // 2. 切分中序遍历数组
    // 3. 切分后序遍历数组
    // 关键:后序遍历数组的切分长度和中序遍历序列的长度一样

    public TreeNode buildTree(int[] inorder, int[] postorder) {

        TreeNode root = buildTree(inorder, postorder, 
                                    0, inorder.length - 1, 
                                    0, postorder.length - 1);
        return root;
    }

    public TreeNode buildTree(
        int[] inorder, int[] postorder, 
        int inorderStartIndex, int inorderEndIndex, 
        int postorderStartIndex, int postorderEndIndex) 
    {
        if(inorderStartIndex > inorderEndIndex)
            return null;

        if(inorderStartIndex == inorderEndIndex)
            return new TreeNode(inorder[inorderStartIndex]);
        
        int rootValue = postorder[postorderEndIndex];
        TreeNode root = new TreeNode(rootValue);

        int inoderDivideIndex = inorderStartIndex;
        for(inoderDivideIndex = inorderStartIndex; inoderDivideIndex < inorderEndIndex; inoderDivideIndex++){
            if(inorder[inoderDivideIndex] == rootValue)
                break;
        }
        
        root.left = buildTree(inorder, postorder, 
        inorderStartIndex, inoderDivideIndex - 1, 
        postorderStartIndex,
        postorderStartIndex + (inoderDivideIndex - inorderStartIndex) - 1
        );
        root.right = buildTree(inorder, postorder, 
        inoderDivideIndex + 1, inorderEndIndex,
        postorderStartIndex + (inoderDivideIndex - inorderStartIndex),
        postorderEndIndex - 1
        );
        return root;
    }
}

13. 从前序与中序遍历序列构造二叉树(LeetCode 105)

思路和上一题类似

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder.length == 0)
            return null;
        
        TreeNode root = buildTree(preorder, inorder, 0, preorder.length-1, 0, inorder.length - 1);
        return root;
    }

    public TreeNode buildTree(int[] preorder, int[] inorder,
                            int preStart, int preEnd,
                            int inStart, int inEnd
    ) {
        if(preStart > preEnd)
            return null;
        if(preStart == preEnd)
            return new TreeNode(inorder[inStart]);
        
        int rootVal = preorder[preStart];
        TreeNode root = new TreeNode(rootVal);

        int inDivide = inStart;
        for(;inDivide < inEnd; ++inDivide){
            if(inorder[inDivide] == rootVal)
                break;
        }
        int leftLength = inDivide - inStart;
        root.left = buildTree(preorder, inorder, 
                            preStart + 1, preStart + leftLength,
                            inStart, inStart + leftLength - 1
        );
        root.right = buildTree(preorder, inorder, 
                            preStart + leftLength + 1, preEnd,
                            inDivide + 1, inEnd
        );

        return root;
    }
}

14. 构造最大二叉树(LeetCode 654)

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

  1. 创建一个根节点,其值为 nums 中的最大值。
  2. 递归地在最大值 左边 的 子数组前缀上 构建左子树。
  3. 递归地在最大值 右边 的 子数组后缀上 构建右子树。
  4. 返回 nums 构建的 最大二叉树 。
输入: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 的节点。
        - 空数组,无子节点。
class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        if(nums.length == 0)
            return null;
        TreeNode root = constructMaximumBinaryTree(nums, 0, nums.length-1);
        return root;
    }

    public TreeNode constructMaximumBinaryTree(int[] nums, int start, int end){
        if(start > end)
            return null;
        if(start == end)
            return new TreeNode(nums[start]);
        
        int max = nums[start];
        int maxIndex = start;
        for(int i = start; i <= end; ++i){
            if(nums[i] > max){
                max = nums[i];
                maxIndex = i;
            }
        }

        TreeNode root = new TreeNode(max);
        root.left = constructMaximumBinaryTree(nums, start, maxIndex-1);
        root.right = constructMaximumBinaryTree(nums, maxIndex + 1, end);
        return root;
    }
}

15. 合并二叉树(LeetCode 617)

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

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。
你需要将这两棵树合并成一棵新二叉树。合并的规则是:

  • 如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;
  • 否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

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

class Solution {
    // 直接在原二叉树上进行修改
    // 新二叉树base在子树1上
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1 == null && root2 == null)
            return null;
        else if(root1 != null && root2 == null)
            return root1;
        else if(root1 == null && root2 != null)
            return root2;
        else {
            root1.val += root2.val;
            root1.left = mergeTrees(root1.left, root2.left);
            root1.right = mergeTrees(root1.right, root2.right);
            return root1;
        }
    }
}

16. 二叉搜索树是否合法(LeetCode 98)

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

  1. 左子树的所有节点值 < 根节点值
  2. 右子树的所有节点值 > 根节点值
  3. 左右子树都是二叉搜索树

重要性质:二叉搜索树的中序遍历序列是递增的。

// 方法一
class Solution {
    // 二叉搜索树的中序遍历序列应该是递增的
    public boolean isValidBST(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        inorderTraverse(root, result);
        // 检查中序序列是否是递增的
        for(int i = 0; i < result.size() - 1; ++i){
            if(result.get(i) >= result.get(i+1))
                return false;
        }
        return true;
    }

    public void inorderTraverse(TreeNode root, List<Integer> result) {
        if(root == null)
            return ;
        inorderTraverse(root.left, result);
        result.add(root.val);
        inorderTraverse(root.right, result);
    }
}

//方法二
class Solution {
    long maxValue = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        if(root == null)
            return true;
        
        boolean leftFlag = isValidBST(root.left);
        if(root.val > maxValue)
            maxValue = root.val;
        else 
            return false;

        boolean rightFlag = isValidBST(root.right);
        
        return leftFlag && rightFlag;
    }
}

17. 二叉搜索树的最小绝对差(LeetCode 530)

给你一棵二叉搜索树,请你返回 树中任意两不同节点值之间的最小绝对差值 。

思路

  • 还是利用二叉搜索树中序遍历递增的性质
  • 可以通过中序遍历,将数值保存到数组中(空间复杂度为O(n))
  • 也可以用pre指针维护前一个访问的节点,每次用cur.val - pre.val (cur即为中序访问的root)即可得到两个节点之间的绝对差值
class Solution {
    // 二叉搜索树的中序遍历序列是递增的
    // 在中序遍历的时候 min = Math.min(min, root.val - pre.val);
    int min = Integer.MAX_VALUE;
    TreeNode pre = null;
    public int getMinimumDifference(TreeNode root) {
        inorderTraverse(root);
        return min;
    }

    public void inorderTraverse(TreeNode root) {
        if(root == null)
            return ;
        // 左
        inorderTraverse(root.left);
        // 中
        if(pre != null)
            min = Math.min(min, root.val - pre.val);
        pre = root;
        // 右
        inorderTraverse(root.right);
        return ;
    }
}

18. 二叉搜索树中的众数(LeetCode 501)

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。

注意

  • 如果count > maxCount是,原先存入result的元素都需要clear()移除
  • 在处理中序节点的时候,记得更新pre
  • ArrayList转化为int[],似乎不能通过toArray直接转,需要通过一次循环
class Solution {
    List<Integer> result = new ArrayList<Integer>();
    TreeNode pre = null;
    int maxCount = 0;
    int count = 0;
    public int[] findMode(TreeNode root) {
        inorderTraverse(root);
        int[] array = new int[result.size()];
        for(int i = 0; i < result.size() ; ++i) {
            array[i] = result.get(i);
        }
        return array;
    }

    public void inorderTraverse(TreeNode root) {
        if(root == null)
            return ;
        
        inorderTraverse(root.left);

        if(pre == null)
            count = 1;
        else if(pre.val != root.val)
            count = 1;
        else 
            count++;
        
        if(count == maxCount) {
            result.add(root.val);
        }
        // 如果出现大于maxCount的元素,则之前result中的众数都作废
        if(count > maxCount) {
            result.clear();
            result.add(root.val);
            maxCount = count;
        }
        // 更新pre
        pre = root;

        inorderTraverse(root.right);
        return ;
    }
}

19. 二叉树的最近公共祖先(LeetCode 236) (有难度)

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

百度百科中最近公共祖先的定义为:
“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,
满足 x 是 p、q 的祖先且 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;
        else if(left != null && right == null)
            return left;
        else if(left == null && right != null)
            return right;
        else 
            return null;
    }
}

20. 二叉搜索树的最近公共祖先(LeetCode 235)

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

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

注意

  • 二叉搜索树自带方向,不需要通过回溯,只需要判断root.valp.val, q.val的大小关系即可
class Solution {
    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;
    }
}

21. 二叉搜索树中插入一个节点(LeetCode 701)

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

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

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if(root == null)
            return new TreeNode(val);
        
        if(val < root.val)
            root.left = insertIntoBST(root.left, val);
        if(val > root.val)
            root.right = insertIntoBST(root.right, val);

        return root;
    }
}

22. 二叉搜索树中删除一个节点(LeetCode 450)(有难度)

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

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

  1. 首先找到需要删除的节点;
  2. 如果找到了,删除它。
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        // 终止条件
        if(root == null)
            return null;
        if(key == root.val) {
            if(root.left == null && root.right == null)
                return null;
            else if(root.left != null && root.right == null)
                return root.left;
            else if(root.left == null && root.right != null)
                return root.right;
            // 待删除的节点左右都不为空
            else {
                TreeNode cur = root.right;
                while(cur.left != null)
                    cur = cur.left;
                // 把待删除节点的左子树 root.left
                // 挂到待删除节点的右子树中最小的节点的左边 cur.left
                cur.left = root.left;
                // root.right代替root原先的位置
                return root.right;
            }
        }

        //递归逻辑
        if(key < root.val)
            root.left = deleteNode(root.left, key);
        if(key > root.val)
            root.right = deleteNode(root.right, key);
        return root;
    }
}

23. 修剪二叉搜索树(LeetCode 669)

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

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

思路

  • 删除二叉搜索树中的节点的逻辑类似
  • 删除某个节点后,终结条件中用新节点作为返回值,作为被删除节点的父节点的新孩子
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if(root == null)
            return null;

        if(root.val < low)
            return trimBST(root.right, low, 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;
    }
}

24. 将有序数组转换为二叉搜索树(LeetCode 108)

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

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return sortedArrayToBST(nums, 0, nums.length-1);
    }

    public TreeNode sortedArrayToBST(int[] nums, int start, int end) {
        if(end < start)
            return null;
        
        int rootVal = nums[(start + end) / 2];
        TreeNode root = new TreeNode(rootVal);
        root.left = sortedArrayToBST(nums, start, (start + end) / 2 - 1);
        root.right = sortedArrayToBST(nums, (start + end) / 2 + 1, end);
        return root;
    }
}

25. 将二叉搜索树转化为累加树(LeetCode 538)

给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),
使得每个节点的值是原来的节点值加上所有大于它的节点值之和。

思路

  • 对于递增数组[2, 5, 13]的累加和是[20,18,13],从后往前求
  • 对应二叉搜索树的访问顺序是 右-中-左
class Solution {
    int sum = 0;
    public TreeNode convertBST(TreeNode root) {
        traverse(root);
        return root;
    }


    public void traverse(TreeNode root) {
        if(root == null)
            return ;
        
        traverse(root.right);
        sum += root.val;
        root.val = sum;
        traverse(root.left);
        return ;
    }
}
posted @ 2024-07-15 21:57  Frank23  阅读(7)  评论(0编辑  收藏  举报