diff --git a/src/main/java/graph/NumIslands.java b/src/main/java/graph/NumIslands.java index e5f1227..6d354af 100644 --- a/src/main/java/graph/NumIslands.java +++ b/src/main/java/graph/NumIslands.java @@ -9,7 +9,7 @@ package graph; * 链接:https://leetcode.cn/problems/number-of-islands/ */ - +//二刷会做 public class NumIslands { void dfs(char[][] grid, int r, int c) { int nr = grid.length; diff --git a/src/main/java/graph/OrangesRotting.java b/src/main/java/graph/OrangesRotting.java index 23673f3..e5eca87 100644 --- a/src/main/java/graph/OrangesRotting.java +++ b/src/main/java/graph/OrangesRotting.java @@ -16,6 +16,7 @@ import java.util.Queue; */ //思想会,代码写不利索 +//二刷会 public class OrangesRotting { public int orangesRotting(int[][] grid) { int rows = grid.length; diff --git a/src/main/java/tree/LowestCommonAncestor.java b/src/main/java/tree/LowestCommonAncestor.java index 12fda7e..b1ade5c 100644 --- a/src/main/java/tree/LowestCommonAncestor.java +++ b/src/main/java/tree/LowestCommonAncestor.java @@ -2,6 +2,8 @@ package tree; import java.util.HashMap; import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** * 题目: 236. 二叉树的最近公共祖先 (lowestCommonAncestor) @@ -12,36 +14,64 @@ import java.util.HashSet; * 链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/ */ +//二刷不会,很妙的题 public class LowestCommonAncestor { - void helper(HashMap map, TreeNode father, TreeNode cur) { - if (cur == null) + + /** + * 先序遍历整棵树,把每个节点的父节点记录到 map 里: + * key = 当前节点 + * value = 它的父节点(根节点对应 null) + * + * @param parentMap 存放节点到其父节点的映射 + * @param parent 当前节点的父节点 + * @param node 要处理的当前节点 + */ + private void buildParentMap(Map parentMap, TreeNode parent, TreeNode node) { + if (node == null) { return; - map.put(cur, father); - helper(map, cur, cur.left); - helper(map, cur, cur.right); + } + // 记录当前节点的父节点 + parentMap.put(node, parent); + + // 递归处理左、右子树 + buildParentMap(parentMap, node, node.left); + buildParentMap(parentMap, node, node.right); } + /** + * 返回二叉树中节点 p 和 q 的最近公共祖先 + * + * @param root 根节点 + * @param p 第一个目标节点 + * @param q 第二个目标节点 + * @return 最近公共祖先节点 + */ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { - HashMap map = new HashMap<>(); - HashSet set = new HashSet<>(); - map.put(root, null); - helper(map, root, root.left); - helper(map, root, root.right); + // 1. 构建 parentMap:每个节点指向它的父节点 + Map parentMap = new HashMap<>(); + parentMap.put(root, null); // 根节点没有父节点 + buildParentMap(parentMap, root, root.left); + buildParentMap(parentMap, root, root.right); - // 将 p 以及其所有祖先加入 set - TreeNode tp = p; - while (tp != null) { - set.add(tp); - tp = map.get(tp); + // 2. 把 p 自身以及它所有的祖先都放到一个 set 里 + Set ancestorsOfP = new HashSet<>(); + TreeNode cur = p; + while (cur != null) { + ancestorsOfP.add(cur); + cur = parentMap.get(cur); // 一步步往上找父节点 } - // 从 q 开始,找到第一个出现在 p 祖先链中的节点 - tp = q; - while (tp != null) { - if (set.contains(tp)) - return tp; - tp = map.get(tp); + // 3. 从 q 开始往上,找到第一个在 ancestorsOfP 中出现的节点 + cur = q; + while (cur != null) { + if (ancestorsOfP.contains(cur)) { + // 第一次相遇的节点就是最近公共祖先 + return cur; + } + cur = parentMap.get(cur); } - return null; // 理论上不可能执行到这里,因为肯定存在公共祖先 + + // 理论上不会执行到这里,因为二叉树必定存在公共祖先 + return null; } } diff --git a/src/main/java/tree/MaxPathSum.java b/src/main/java/tree/MaxPathSum.java new file mode 100644 index 0000000..eff7700 --- /dev/null +++ b/src/main/java/tree/MaxPathSum.java @@ -0,0 +1,45 @@ +package tree; +/** + * 题目: 124. 二叉树中的最大路径和 (maxPathSum) + * 描述:二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。 + * 路径和 是路径中各节点值的总和。 + * 给你一个二叉树的根节点 root ,返回其 最大路径和 。 + + + * 链接:https://leetcode.cn/problems/binary-tree-maximum-path-sum/ + + */ +//二刷不会 +public class MaxPathSum { + /** + * 计算当前节点的 最大路径和 可以包含左右节点,但最大贡献度不能同时包括左右节点。因为对于某节点,他的贡献只能带其中一个孩子(带大孩子走),整个路径才能向上延伸!! + * priceNewpath = node.val + leftGain + rightGain:把当前节点当作「拐点」,将左右子树的最大非负贡献都连上,再加上自身,得到一条完整过顶路径的和,用它更新全局最大值。 + * return node.val + Math.max(leftGain, rightGain):向上只传递一条最优子路径的贡献(自身 + 较大子贡献),因为合法路径不能同时分叉到左右两边。 + */ + int maxSum = Integer.MIN_VALUE; + + public int maxPathSum(TreeNode root) { + maxGain(root); + return maxSum; + } + + public int maxGain(TreeNode node) { + if (node == null) { + return 0; + } + + // 递归计算左右子节点的最大贡献值 + // 只有在最大贡献值大于 0 时,才会选取对应子节点 + int leftGain = Math.max(maxGain(node.left), 0); + int rightGain = Math.max(maxGain(node.right), 0); + + // 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值 + int priceNewpath = node.val + leftGain + rightGain; + + // 更新答案 + maxSum = Math.max(maxSum, priceNewpath); + + // 返回节点的最大贡献值 + return node.val + Math.max(leftGain, rightGain); + } +} diff --git a/src/main/java/tree/PathSum.java b/src/main/java/tree/PathSum.java index 578cbec..b7921ea 100644 --- a/src/main/java/tree/PathSum.java +++ b/src/main/java/tree/PathSum.java @@ -14,6 +14,7 @@ import java.util.Map; //前缀和想到了,代码不会写 //二刷不会 public class PathSum { + /** * prefix 这张哈希表只应该保存“当前递归栈(从根结点到正在访问的那个结点)”这条路径上出现过的前缀和。!!!! * 当递归函数从一个结点返回到它的父结点时,这个结点及其子树已经遍历完毕,它对应的前缀和在后面的兄弟子树中就再也不会出现在