数据结构Java版:二叉树(题目篇)

相关文章

数据结构Java版:二叉树(基础篇)

一、简介

本篇章主要介绍二叉树的一些操作和简单应用,用于巩固和提升对二叉树的理解和使用。

二、练习

1.相同的树

  • 给你两棵二叉树的根节点 p 和 q ,检验这两棵树是否相同

                 1                      1
               /  \        same       /   \      
              2    3                 2     3
              
                 1                       1
                /         not same         \
               3                             3
    
  • 实现

	public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p == null && q == null) {
            return true;
        }
        if (p == null || q == null) {
            return false;
        }
        if (p.val != q.val) {
            return false;
        }
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }

2.翻转二叉树

  • 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

              1                           1  
         2       3       =>          3       2
       4  5     6  7               7  6     5  4  
    
  • 实现

	public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        dfs(root);
        return root;
    }

    // 先序遍历 对每一个节点做 子节点交换处理
    private void dfs(TreeNode root) {

        if (root == null) {
            return;
        }

        // swap过程
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;

        dfs(root.left);
        dfs(root.right);
    }

3.对称二叉树

  • 判断给定的二叉树是否是对称二叉树

               1
            /     \
          2         2
        /  \      /   \
       3    5    5     3
       输出: true
    
  • 方法一:先对根节点左孩子做翻转操作,在比较翻转后的左孩子结果和右孩子是否一样。

	public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }
        TreeNode left = bfs(root.left);
        return isSameTree(left, root.right);
    }

    // 翻转
    private TreeNode bfs(TreeNode root) {
        if (root == null) {
            return null;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        TreeNode curNode = root;
        queue.add(curNode);
        TreeNode tmp;
        while (!queue.isEmpty()) {
            curNode = queue.poll();
            tmp = curNode.left;
            curNode.left = curNode.right;
            curNode.right = tmp;
            if (curNode.left != null) {
                queue.add(curNode.left);
            }
            if (curNode.right != null) {
                queue.add(curNode.right);
            }
        }
        return root;
    }

    // p、q是否相同
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p == null && q == null) {
            return true;
        }
        if (p == null || q == null) {
            return false;
        }
        if (p.val != q.val) {
            return false;
        }
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
  • 方法二:递归
	public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }
        return judge(root.left, root.right);
    }

    private boolean judge(TreeNode p, TreeNode q) {
        if (p == null && q == null) {
            return true;
        }
        if (p == null || q == null) {
            return false;
        }
        if (p.val != q.val) {
            return false;
        }
        return judge(p.left, q.right) && judge(p.right, q.left);
    }

4.平衡二叉树

4.1 判断平衡二叉树

  • 给定一个二叉树,判断它是否是高度平衡的二叉树。

  • 定义:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

                 1
               /   \
              2     3
                   /  \
                  4    5
             输出:true
    
  • 实现

	public boolean isBalanced(TreeNode root) {
        if (root == null) {
            return true;
        }
        return judge(root) > 0;
    }

    // 如果是平衡二叉树,返回深度,否则返回-1
    private int judge(TreeNode root) {
        if (root == null) {
            return 0;
        }
        // 左右子树的深度
        int left = judge(root.left);
        int right = judge(root.right);
        if (left == -1 || right == -1 || Math.abs(left - right) > 1) {
            return -1;
        }
        return Math.max(left, right) + 1;
    }

5.二叉搜索树

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

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

5.1 验证二叉搜索树

判断给定树是否是一个有效的二叉搜索树。

	                3
                  /   \
                2       5
                  \
                   4
                输出: false
  1. 方法一:中序遍历,始终有序
	// dfs
    public boolean isValidBST(TreeNode root) {
        if (root == null) {
            return true;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode curNode = root;
        int preVal = Integer.MIN_VALUE;
        boolean startFlag = false;
        while (curNode != null || !stack.empty()) {
            while (curNode != null) {
                stack.push(curNode);
                curNode = curNode.left;
            }
            curNode = stack.pop();
            // System.out.println(curNode.val);
            if (!startFlag) { // 第一个元素
                startFlag = true;
            } else { // 第一个元素不同preVal作比较
                if (curNode.val <= preVal) { // 非有序
                    return false;
                }
            }
            preVal = curNode.val;
            // System.out.println("preVal: " + preVal);
            curNode = curNode.right;
        }
        return true;
    }
  1. 方法二:递归判断每个子树是否在合理的取值区间
	public boolean isValidBST02(TreeNode root) {
        if (root == null) {
            return true;
        }
        return isValid(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }

    private boolean isValid(TreeNode root, long min, long max) {
        if (root == null) {
            return true;
        }
        if (root.val <= min || root.val >= max) {
            return false;
        }
        // 左子树的值在 [min,root.val)
        // 右子树的值在 (root.val, max]
        return isValid(root.left, min, root.val) && isValid(root.right, root.val, max);
    }

5.2 不同的二叉搜索树

-描述:给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同 二叉搜索树 。

示例:n=3
  1             1              2            3           3
    \             \          /  \         /           /
      2             3       1    3       2           1
       \           /                    /             \
         3        2                    1               2

-分析

	 /*
	    一、以i为根节点
	        1、i的左面 [1,i]    对应不同的二叉搜索树集合 leftList
	        2、i的右面 [i+1,n]  对应不同的二叉搜索树集合 rightList
	        3、以i为根节点的 不同二叉搜索树为 leftList 和 rightList 的笛卡尔积组合 => output
	    二、所有的不同的二叉搜索树集合
	        for i in [1,n] union(output)

    例如 n=2
        以1为根节点
            left  [1,i-1] => [1,0] => [null]
            right [i+1,n] => [2,2] => [Node(2)]
                =>           1
                           /   \
                         null   2
        以2为根节点
            left  [1,i-1] => [1,1] => [Node(1)]
            right [i+1,n] => [3,2] => [null]
                =>           2
                           /   \
                          1   null
     */
  • 实现
	public List<TreeNode> generateTrees(int n) {
        if (n == 0) {
            return new ArrayList<>();
        }
        return generate(1, n);
    }

    private List<TreeNode> generate(int left, int right) {

        List<TreeNode> output = new ArrayList<>();

        if (left > right) {
            output.add(null);
            return output;
        }
        if (left == right) {
            output.add(new TreeNode(left));
            return output;
        }

        // 以每个i作为根节点
        for (int i = left; i <= right; i++) {
            List<TreeNode> leftList = generate(left, i - 1);
            List<TreeNode> rightList = generate(i + 1, right);
            for (TreeNode l : leftList) {
                for (TreeNode r : rightList) {
                    TreeNode root = new TreeNode(i);
                    root.left = l;
                    root.right = r;
                    output.add(root);
                }
            }
        }

        return output;
    }

5.3 恢复二叉搜索树

  • 描述:给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。

       /*
                       1                        3
                     /                        /
                   3            =>           1
                    \                         \
                     2                         2
      */
    
  • 思路:二叉搜索树是中序遍历有序的,所以
    1、中序遍历二叉树,将结果存入List中。
    2、对List做排序。
    3、中序遍历重新给二叉树填值。
    说明:空间复杂度O(n),且做了两次完整的遍历。

  • 实现

	public void recoverTree(TreeNode root) {
        if (root == null) {
            return;
        }
        List<Integer> list = new ArrayList<>();
        dfs(root, list);
        Collections.sort(list);
        recover(root, list);
    }

    // dfs
    private void dfs(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        dfs(root.left, list);
        list.add(root.val);
        dfs(root.right, list);
    }

    // dfs
    private void recover(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode curNode = root;
        int idx = 0;
        while (curNode != null || !stack.empty()) {
            while (curNode != null) {
                stack.push(curNode);
                curNode = curNode.left;
            }
            curNode = stack.pop();
            curNode.val = list.get(idx++);
            curNode = curNode.right;
        }
    }
  • 改进:其实大可不必做两次完全遍历。
    1、中序遍历二叉树,将结果存入List中。
    2、找到 单调递增数组中被交换的两个数 (场景会比较受限,不能存在重复元素)。
    3、中序遍历调整二叉树中对应的两个被交换的节点值。两个节点值都被恢复后结束恢复程序。
    4、存在更优化的解法,感兴趣的朋友可以去做进一步了解。
  • 实现
	public void recoverTree(TreeNode root) {
        if (root == null) {
            return;
        }
        List<Integer> list = new ArrayList<>();
        dfs(root, list);
        // 被交换的两个数
        int[] swappedNums = findSwapTwoNum(list);
        recover(root, swappedNums[0], swappedNums[1], 2);
    }

    // dfs
    private void dfs(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        dfs(root.left, list);
        list.add(root.val);
        dfs(root.right, list);
    }

    /*
    [1,3,4,5,6,7] => [1,6,4,5,3,7]
     */
    public int[] findSwapTwoNum(List<Integer> list) {
        int preNumIdx = -1; // 第一个被交换数的位置
        int postNumIdx = -1; // 第二个被交换数的位置
        for (int i = 0; i < list.size() - 1; i++) {
            if (list.get(i) > list.get(i + 1)) {
                postNumIdx = i + 1;
                if (preNumIdx == -1) { // 此前第一个被交换的数还未找到
                    preNumIdx = i;
                } else {
                    break;
                }
            }
        }
        return new int[]{list.get(preNumIdx), list.get(postNumIdx)};
    }

    private void recover(TreeNode root, Integer preNum, Integer postNum, int handleTimes) {
        if (root == null) {
            return;
        }
        if (handleTimes == 0) {
            return;
        }
        recover(root.left, preNum, postNum, handleTimes);
        // 重新设置两个节点值
        if (root.val == preNum) {
            root.val = postNum;
            handleTimes--;
        } else if (root.val == postNum) {
            root.val = preNum;
            handleTimes--;
        }
        recover(root.right, preNum, postNum, handleTimes);
    }

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

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

     /*
                                     3
                                   /   \
     [0,1,2,3,4,5]  =>           1      5
                                / \    /
                               0   2  4
     说明:结果不唯一
    

    */

  • 思路:
    1、选取数组中间元素(偶数选偏右)

    				[0,1...n] => 
    					left[0,mid-1]
    					[mid]
    					right[mid+1,right]
    			     => Node(nums[mid])
                         /         \
      toBST(nums[0...mid-1])       toBST(nums[mid+1...right])
    

    2、中间元素生成根节点,左右子数组又为子问题。递归到子数组为一个元素或者越界。

  • 实现

	public TreeNode sortedArrayToBST(int[] nums) {
        if (nums.length == 0) {
            return null;
        }
        return toBST(nums, 0, nums.length - 1);
    }

    private TreeNode toBST(int[] nums, int left, int right) {

        if (left > right) {
            return null;
        }

        if (left == right) {
            return new TreeNode(nums[left]);
        }

        int mid = (left + right + 1) / 2;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = toBST(nums, left, mid - 1);
        root.right = toBST(nums, mid + 1, right);
        return root;
    }

5.5 将有序链表转换为二叉搜索树

  • 说明:思路同数组,待更新链表后更新此篇。

6.构造二叉树

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

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

  • preorder 和 inorder 均 无重复 元素

     /*
                 1
               /   \
              3     5
                   / \
                  7   9
    
         输入: preorder = [1,3,5,7,9], inorder = [3,1,7,5,9]
      */
    
  • 思路
    1、取preorder首个元素num为根,在对应的inorder中寻找num的位置。以上述输入为例子
    2、[3]&&[3] 是左子树;[5,7,9]&&[7,5,9]是右子树。
    3、原址计算,需要标记preorder的左右边界和inorder的左右边界;递归。

  • 实现

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

        int n = preorder.length;
        Map<Integer, Integer> map = arrayToMap(inorder);
        return build(preorder, inorder, map, 0, n - 1, 0, n - 1);
    }

    /**
     * 根据先序遍历和中序遍历构建二叉树
     *
     * @param preorder 先序遍历
     * @param inorder  中序遍历
     * @param map      中序遍历map
     * @param pl       preorder left
     * @param pr       preorder right
     * @param il       inorder left
     * @param ir       inorder right
     * @return
     */
    private TreeNode build(int[] preorder, int[] inorder, Map<Integer, Integer> map, int pl, int pr, int il, int ir) {

        if (pl > pr) {
            return null;
        }

        if (pl == pr) {
            return new TreeNode(preorder[pl]);
        }

        int num = preorder[pl];
        TreeNode root = new TreeNode(num);
        int idx = map.get(num);

        // 1.中序遍历num左侧元素个数
        // 2.int range = idx - il;
        // 3.即从 pl + 1 到新的 npr 之间有 idx - il 个元素
        // 4.所以 npr - (pl + 1) + 1 = range = idx - il
        // 5.得出 npr = idx + pl -il

        System.out.println(String.format("[num=%s]", num));
        System.out.println(String.format("[left][%s-%s][%s-%s]", pl + 1, idx + pl - il, il, idx - 1));
        System.out.println(String.format("[right][%s-%s][%s-%s]", idx + pl - il + 1, pr, idx + 1, ir));

        root.left = build(preorder, inorder, map, pl + 1, idx + pl - il, il, idx - 1);
        root.right = build(preorder, inorder, map, idx + pl - il + 1, pr, idx + 1, ir);

        return root;
    }

    private Map<Integer, Integer> arrayToMap(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], i);
        }
        return map;
    }

7.二叉树路径

7.1 二叉树的所有路径

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

     /*
                     1
                   /   \
                  2     3
                   \
                    5
     输入:root = [1,2,3,null,5]
     输出:["1->2->5","1->3"]
     */
    
  • 方法一:广度优先搜索(一)
    思路:
    1、层级遍历,记录每一个节点的父节点,parentMap
    2、当左右孩子都是null的时候,到达路径终点。
    3、从叶子节点反向追踪到根路径,得到路径。
    实现:

	// 记录每节点的父节点
	private Map<TreeNode, TreeNode> parentMap = new HashMap<>();

    public List<String> binaryTreePaths(TreeNode root) {
        List<String> pathList = new ArrayList<>();
        if (root == null) {
            return pathList;
        }
        genTreePaths(root, pathList);
        return pathList;
    }

    private void genTreePaths(TreeNode root, List<String> pathList) {
        Queue<TreeNode> queue = new LinkedList<>();
        TreeNode curNode = root;
        queue.add(curNode);
        while (!queue.isEmpty()) {
            curNode = queue.poll();
            if (curNode.left == null && curNode.right == null) {
                pathList.add(buildPath(curNode, parentMap));
            } else {
                if (curNode.left != null) {
                    queue.add(curNode.left);
                    parentMap.put(curNode.left, curNode);
                }
                if (curNode.right != null) {
                    queue.add(curNode.right);
                    parentMap.put(curNode.right, curNode);
                }
            }
        }
    }

    // build path
    private String buildPath(TreeNode node, Map<TreeNode, TreeNode> parentMap) {
        List<Integer> path = new ArrayList<>();
        while (node != null) {
            path.add(node.val);
            node = parentMap.get(node);
        }
        Collections.reverse(path);
        return path.stream().map(String::valueOf).collect(Collectors.joining("->"));
    }
  • 方法二:广度优先搜索(二)
    思路:
    1、层级遍历,因为每个节点都有与之一一对应的路径,设一个和节点树结构一样的路径"树"(虚拟的树 queue)

     	    /*
                 1                             1
               /   \                        /      \
              2     3        =>           1->2     1->3
               \                            \
                5                           1->2->5
             (nodeQueue)                    (pathQueue)
            */
    

    2、每次取节点时,同步更新路径到pathQueue,左右孩子都为null到达路径终点。
    3、取叶子节点对应的路径。
    实现:

	public List<String> binaryTreePaths(TreeNode root) {
        List<String> pathList = new ArrayList<>();
        if (root == null) {
            return pathList;
        }
        bfs(root, pathList);
        return pathList;
    }

    // bfs
    private void bfs(TreeNode root, List<String> pathList) {

        Queue<TreeNode> nodeQueue = new LinkedList<>();
        Queue<String> pathQueue = new LinkedList<>();
        TreeNode curNode = root;
        String curPath = String.valueOf(curNode.val);
        nodeQueue.add(curNode);
        pathQueue.add(curPath);
        while (!nodeQueue.isEmpty()) {
            curNode = nodeQueue.poll();
            curPath = pathQueue.poll();
            if (curNode.left == null && curNode.right == null) {
                pathList.add(curPath);
            } else {
                if (curNode.left != null) {
                    nodeQueue.add(curNode.left);
                    pathQueue.add(new StringBuffer(curPath).append("->").append(curNode.left.val).toString());
                }
                if (curNode.right != null) {
                    nodeQueue.add(curNode.right);
                    pathQueue.add(new StringBuffer(curPath).append("->").append(curNode.right.val).toString());
                }
            }
        }
    }
  • 方法三:深度优先搜索
    思路同方法二
    实现:
	public List<String> binaryTreePaths(TreeNode root) {
        List<String> pathList = new ArrayList<>();
        if (root == null) {
            return pathList;
        }
        // bfs(root, pathList);
        dfs(root, new StringBuffer(), pathList);
        return pathList;
    }

    // dfs
    private void dfs(TreeNode root, StringBuffer pathBuffer, List<String> pathList) {
        pathBuffer.append(root.val);
        if (root.left == null && root.right == null) {
            pathList.add(pathBuffer.toString());
        } else { // 左右孩子存在不为空的情况
            pathBuffer.append("->");
            if (root.left != null) {
                dfs(root.left, new StringBuffer(pathBuffer), pathList);
            }
            if (root.right != null) {
                dfs(root.right, new StringBuffer(pathBuffer), pathList);
            }
        }
    }

7.2 路径总和

  • 描述:给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
  • 分析:在已经掌握求解二叉树所有路径的情况下,想要解出此题已是手到擒来,但本题 仅仅要求判断路径是否存在,如此便不需要先遍历出所有的路径,而是采取深度搜索的算法。
  • 实现:
	public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return false;
        }
        return search(root, targetSum);
    }

    private boolean search(TreeNode root, int targetSum) {

        if (root.left == null && root.right == null) {
            return root.val == targetSum;
        }

        targetSum -= root.val;
        boolean left = false, right = false;
        if (root.left != null) {
            left = search(root.left, targetSum);
        }
        if (root.right != null) {
            right = search(root.right, targetSum);
        }
        return left || right;
    }
思考:也可以使用回溯法或者bfs,间隔一段时间,自己每次写出来的也不一样。

7.3 路径总和II

  • 给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
  • 本次只展示dfs的写法。
  • 方法一
	public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        List<Integer> path = new ArrayList<>();
        dfs(root, targetSum, path, res);
        return res;
    }

    private void dfs(TreeNode root, int targetSum, List<Integer> path, List<List<Integer>> res) {

        path.add(root.val);
        targetSum -= root.val;
        if (root.left == null && root.right == null) {
            if (targetSum == 0) {
                res.add(path);
            }
        } else {
            if (root.left != null) {
                dfs(root.left, targetSum, new ArrayList<>(path), res);
            }
            if (root.right != null) {
                dfs(root.right, targetSum, new ArrayList<>(path), res);
            }
        }
    }
  • 方法一改进
    1、方法一在递归子问题的时候,每次都new新的List,空间开销较大。
    2、改用回溯的方法,使用同一个List实例。
	public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        List<Integer> path = new ArrayList<>();
        dfs2(root, targetSum, path, res);
        return res;
    }

	private void dfs2(TreeNode root, int targetSum, List<Integer> path, List<List<Integer>> res) {

        // add
        path.add(root.val);
        targetSum -= root.val;
        if (root.left == null && root.right == null) {
            if (targetSum == 0) {
                res.add(new ArrayList<>(path));
            }
        }

        if (root.left != null) {
            dfs2(root.left, targetSum, path, res);
        }
        if (root.right != null) {
            dfs2(root.right, targetSum, path, res);
        }

        // remove
        path.remove(path.size() - 1);
    }

7.4 路径总和III

  • 描述:给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
在这里插入图片描述

  • 思路:双层递归

    1、外层递归循环遍历到每个节点。以每个节点作为根节点,执行步骤2.
    2、从根节点出发,搜索路径和为targetSum的路径。

  • 实现

	// 路径计数
	private int count = 0;

    public int pathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return 0;
        }
        dfs(root, targetSum);
        return count;
    }

    // 1、尝试以每一个节点为根
    private void dfs(TreeNode root, int targetSum) {
        if (root == null) {
            return;
        }
        // 内层递归
        // bfs(root, targetSum);
        dfsSearch(root, targetSum);
        dfs(root.left, targetSum);
        dfs(root.right, targetSum);
    }

    // 2、从根节点搜索targetSum
    private void dfsSearch(TreeNode root, int targetSum) {
        if (root == null) {
            return;
        }
        targetSum -= root.val;
        if (targetSum == 0) {
            count++;
        }
        dfsSearch(root.left, targetSum);
        dfsSearch(root.right, targetSum);
    }

	// 方法二
    private void bfs(TreeNode root, int targetSum) {
        if (root == null) {
            return;
        }
        // 节点队列
        Queue<TreeNode> nodeQueue = new LinkedList<>();
        TreeNode curNode = root;
        // System.out.println(String.format("[root:%s]", root.val));
        nodeQueue.add(curNode);
        // 路径和队列
        Queue<Integer> sumQueue = new LinkedList<>();
        Integer sum = 0;
        sumQueue.add(sum);
        while (!nodeQueue.isEmpty()) {
            curNode = nodeQueue.poll();
            sum = sumQueue.poll() + curNode.val;
            // System.out.println(String.format("[curNode:%s][sum:%s]", curNode.val, sum));
            if (sum == targetSum) { // 寻找目标5. 3->2 还可能有 3->2->-4->4
                count++;
            }
            if (curNode.left != null) {
                nodeQueue.add(curNode.left);
                sumQueue.add(sum);
            }
            if (curNode.right != null) {
                nodeQueue.add(curNode.right);
                sumQueue.add(sum);
            }
        }
    }
  • 提示:存在更优的前缀和解法,请感兴趣的读者自行思考实现。

7.5 二叉树中的最大路径和

  • 描述:路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
    在这里插入图片描述
    输出:42

  • 思路
    1、遍历每个节点作为根节点。
    2、计算根节点和左右孩子能够组合成的最大值。注意:该组合一定包含根,不一定包含左右子树。
    3、取根节点和 左右孩子中”贡献值“大的一方作为一条子路径返回。比如 上图的 15 20(root) 7 组合,返回 15+20

  • 实现

	// 最大值
 	private int max = Integer.MIN_VALUE;

    public int maxPathSum(TreeNode root) {
        if (root == null) {
            return 0;
        }
        dfsForMaxSum(root);
        return max;
    }

    // 包含根节点的root的最大 左根右 路径和
    private int dfsForMaxSum(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int sum = root.val;
        int left = Math.max(dfsForMaxSum(root.left), 0);
        int right = Math.max(dfsForMaxSum(root.right), 0);

        // 更新最大值 // 包含根节点的root的最大 左根右 路径和
        max = Math.max(max, root.val + left + right);

        // 处理返回值 选取左右子节点贡献大的一方
        sum += Math.max(left, right);
        return sum;
    }

三、心得体会

  • 在熟练掌握二叉树的遍历的基础上,解法自然会多种多样。
  • 本篇写的潦草,没有详细的解题步骤,主要是为了提示自己,重新拾起基础;总结自己的解题思路,题目永远是无限的,方法形成的过程更重要。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值