|
| 1 | +# 题目描述(中等难度) |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | +[112 题](<https://leetcode.wang/leetcode-112-Path-Sum.html>) 的升级版,给定一个`sum`,输出从根节点开始到叶子节点,和为`sum` 的所有路径可能。 |
| 6 | + |
| 7 | +直接在 [112 题](<https://leetcode.wang/leetcode-112-Path-Sum.html>) 的基础上改了,解法没有新内容,大家可以过去看一看。 |
| 8 | + |
| 9 | +# 解法一 递归 |
| 10 | + |
| 11 | +[112 题](<https://leetcode.wang/leetcode-112-Path-Sum.html>) 的解法是下边的样子。 |
| 12 | + |
| 13 | +```java |
| 14 | +public boolean hasPathSum(TreeNode root, int sum) { |
| 15 | + if (root == null) { |
| 16 | + return false; |
| 17 | + } |
| 18 | + return hasPathSumHelper(root, sum); |
| 19 | +} |
| 20 | + |
| 21 | +private boolean hasPathSumHelper(TreeNode root, int sum) { |
| 22 | + //到达叶子节点 |
| 23 | + if (root.left == null && root.right == null) { |
| 24 | + return root.val == sum; |
| 25 | + } |
| 26 | + //左孩子为 null |
| 27 | + if (root.left == null) { |
| 28 | + return hasPathSumHelper(root.right, sum - root.val); |
| 29 | + } |
| 30 | + //右孩子为 null |
| 31 | + if (root.right == null) { |
| 32 | + return hasPathSumHelper(root.left, sum - root.val); |
| 33 | + } |
| 34 | + return hasPathSumHelper(root.left, sum - root.val) || hasPathSumHelper(root.right, sum - root.val); |
| 35 | +} |
| 36 | +``` |
| 37 | + |
| 38 | +这里的话我们需要一个`ans`变量来保存所有结果。一个`temp`变量来保存遍历的路径。需要注意的地方就是,`java`中的`list`传递的是引用,所以递归结束后,要把之前加入的元素删除,不要影响到其他分支的`temp`。 |
| 39 | + |
| 40 | +```java |
| 41 | +public List<List<Integer>> pathSum(TreeNode root, int sum) { |
| 42 | + |
| 43 | + List<List<Integer>> ans = new ArrayList<>(); |
| 44 | + if (root == null) { |
| 45 | + return ans; |
| 46 | + } |
| 47 | + hasPathSumHelper(root, sum, new ArrayList<Integer>(), ans); |
| 48 | + return ans; |
| 49 | +} |
| 50 | + |
| 51 | +private void hasPathSumHelper(TreeNode root, int sum, ArrayList<Integer> temp, List<List<Integer>> ans) { |
| 52 | + // 到达叶子节点 |
| 53 | + if (root.left == null && root.right == null) { |
| 54 | + if (root.val == sum) { |
| 55 | + temp.add(root.val); |
| 56 | + ans.add(new ArrayList<>(temp)); |
| 57 | + temp.remove(temp.size() - 1); |
| 58 | + } |
| 59 | + return; |
| 60 | + } |
| 61 | + // 左孩子为 null |
| 62 | + if (root.left == null) { |
| 63 | + temp.add(root.val); |
| 64 | + hasPathSumHelper(root.right, sum - root.val, temp, ans); |
| 65 | + temp.remove(temp.size() - 1); |
| 66 | + return; |
| 67 | + } |
| 68 | + // 右孩子为 null |
| 69 | + if (root.right == null) { |
| 70 | + temp.add(root.val); |
| 71 | + hasPathSumHelper(root.left, sum - root.val, temp, ans); |
| 72 | + temp.remove(temp.size() - 1); |
| 73 | + return; |
| 74 | + } |
| 75 | + temp.add(root.val); |
| 76 | + hasPathSumHelper(root.right, sum - root.val, temp, ans); |
| 77 | + temp.remove(temp.size() - 1); |
| 78 | + |
| 79 | + temp.add(root.val); |
| 80 | + hasPathSumHelper(root.left, sum - root.val, temp, ans); |
| 81 | + temp.remove(temp.size() - 1); |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +# 解法二 DFS 栈 |
| 86 | + |
| 87 | +[112 题](<https://leetcode.wang/leetcode-112-Path-Sum.html>) 中解法二讲的是`BFS`,但是对于这道题由于我们要保存一条一条的路径,而`BFS`是一层一层的进行的,到最后一层一次性会得到很多条路径。这就导致遍历过程中,我们需要很多`list`来保存不同的路径,对于这道题是不划算的。 |
| 88 | + |
| 89 | +所以这里我们看 [112 题](<https://leetcode.wang/leetcode-112-Path-Sum.html>) 利用栈实现的`DFS`。 |
| 90 | + |
| 91 | +看一下之前用后序遍历实现的代码。 |
| 92 | + |
| 93 | +```java |
| 94 | +public boolean hasPathSum(TreeNode root, int sum) { |
| 95 | + List<Integer> result = new LinkedList<>(); |
| 96 | + Stack<TreeNode> toVisit = new Stack<>(); |
| 97 | + TreeNode cur = root; |
| 98 | + TreeNode pre = null; |
| 99 | + int curSum = 0; //记录当前的累计的和 |
| 100 | + while (cur != null || !toVisit.isEmpty()) { |
| 101 | + while (cur != null) { |
| 102 | + toVisit.push(cur); // 添加根节点 |
| 103 | + curSum += cur.val; |
| 104 | + cur = cur.left; // 递归添加左节点 |
| 105 | + } |
| 106 | + cur = toVisit.peek(); // 已经访问到最左的节点了 |
| 107 | + //判断是否满足条件 |
| 108 | + if (curSum == sum && cur.left == null && cur.right == null) { |
| 109 | + return true; |
| 110 | + } |
| 111 | + // 在不存在右节点或者右节点已经访问过的情况下,访问根节点 |
| 112 | + if (cur.right == null || cur.right == pre) { |
| 113 | + TreeNode pop = toVisit.pop(); |
| 114 | + curSum -= pop.val; //减去出栈的值 |
| 115 | + pre = cur; |
| 116 | + cur = null; |
| 117 | + } else { |
| 118 | + cur = cur.right; // 右节点还没有访问过就先访问右节点 |
| 119 | + } |
| 120 | + } |
| 121 | + return false; |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +和解法一一样,我们需要`ans`变量和`temp`变量,同样需要注意`temp`是对象,是引用传递。 |
| 126 | + |
| 127 | +```java |
| 128 | +public List<List<Integer>> pathSum(TreeNode root, int sum) { |
| 129 | + Stack<TreeNode> toVisit = new Stack<>(); |
| 130 | + List<List<Integer>> ans = new ArrayList<>(); |
| 131 | + List<Integer> temp = new ArrayList<>(); |
| 132 | + TreeNode cur = root; |
| 133 | + TreeNode pre = null; |
| 134 | + int curSum = 0; // 记录当前的累计的和 |
| 135 | + while (cur != null || !toVisit.isEmpty()) { |
| 136 | + while (cur != null) { |
| 137 | + toVisit.push(cur); // 添加根节点 |
| 138 | + curSum += cur.val; |
| 139 | + /************修改的地方******************/ |
| 140 | + temp.add(cur.val); |
| 141 | + /**************************************/ |
| 142 | + cur = cur.left; // 递归添加左节点 |
| 143 | + } |
| 144 | + cur = toVisit.peek(); // 已经访问到最左的节点了 |
| 145 | + // 判断是否满足条件 |
| 146 | + if (curSum == sum && cur.left == null && cur.right == null) { |
| 147 | + /************修改的地方******************/ |
| 148 | + ans.add(new ArrayList<>(temp)); |
| 149 | + /**************************************/ |
| 150 | + } |
| 151 | + // 在不存在右节点或者右节点已经访问过的情况下,访问根节点 |
| 152 | + if (cur.right == null || cur.right == pre) { |
| 153 | + TreeNode pop = toVisit.pop(); |
| 154 | + curSum -= pop.val; // 减去出栈的值 |
| 155 | + /************修改的地方******************/ |
| 156 | + temp.remove(temp.size() - 1); |
| 157 | + /**************************************/ |
| 158 | + pre = cur; |
| 159 | + cur = null; |
| 160 | + } else { |
| 161 | + cur = cur.right; // 右节点还没有访问过就先访问右节点 |
| 162 | + } |
| 163 | + } |
| 164 | + return ans; |
| 165 | +} |
| 166 | +``` |
| 167 | + |
| 168 | +# 总 |
| 169 | + |
| 170 | +和 [112 题](<https://leetcode.wang/leetcode-112-Path-Sum.html>) 没什么区别,主要是注意函数传对象的时候,我们传的不是对象的副本,只是传了一个引用。 |
0 commit comments