二叉樹中和為某一值的路徑(java版)
【題目描述】輸入一顆二叉樹和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。路徑定義為從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。
【解題思路】
根據路徑的定義,選擇樹的先序遍歷作為流程框架。動態儲存根節點到當前節點的path。若當前節點為葉子節點,則判斷路徑和是否為給定的整數值。直到樹的遍歷結束。
【原始碼1】
連結:https://www.nowcoder.com/questionTerminal/b736e784e3e34731af99065031301bca
來源:牛客網
public class Solution {
private ArrayList< ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
private ArrayList<Integer> list = new ArrayList<Integer>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root == null) return listAll;
list .add(root.val);
target -= root.val;
if(target == 0 && root.left == null && root.right == null)
listAll.add(new ArrayList<Integer>(list));
FindPath(root.left, target);
FindPath(root.right, target);
list.remove(list.size()-1);
return listAll;
}
}
a. 程式碼的主要思想即樹的先序遞迴遍歷,用一個arraylist來記錄根節點到當前節點的路徑。
b. list.remove(list.size()-1)
這句話是模擬了棧回退。當前節點為葉子節點或者已經訪問過的情況下,回溯到父節點。
c. listAll.add(new ArrayList<Integer>(list))
這個細節要注意,必須要重新生成一個物件例項,並使用list對其初始化賦值。不可以直接listAll.add(list),因為list本質上是引用,在各次遍歷中會直接改變它的值,最後的路徑集合中前面的路徑也會被後面的覆蓋。再次強調這種做法是錯誤的。
d. 使用target遞減的方式,是一個巧妙的小心思。因為可以把比較的過程直接放在遍歷過程中進行。不必等到訪問到葉子節點,再次對當前路徑的值求和。
e. 有不少網友疑問,“棧回退以後,target不需要重新加回退節點的值嗎?” 針對這個疑問,答案是不需要新增。其原理是,每次遞迴時,當前變數的值都會重新拷貝一份兒,進入到下次遍歷中。也就是說,當前遍歷過程中的各個變數值,會被儲存到當前的環境中。當前遍歷結束,各個變數被銷燬,不會影響到回退的遍歷。即遍歷壓棧是將當前環境壓棧。
【原始碼2】
連結:https://www.nowcoder.com/questionTerminal/b736e784e3e34731af99065031301bca
來源:牛客網
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,
int target) {
ArrayList<ArrayList<Integer>> pathList=
new ArrayList<ArrayList<Integer>>();
if(root==null)
return pathList;
Stack<Integer> stack=new Stack<Integer>();
FindPath(root,target,stack,pathList );
return pathList;
}
private void FindPath(TreeNode root, int target,
Stack<Integer> path,
ArrayList<ArrayList<Integer>> pathList) {
if(root==null)
return;
if(root.left==null&&root.right==null){
if(root.val==target){
ArrayList<Integer> list=
new ArrayList<Integer>();
for(int i:path){
list.add(new Integer(i));
}
list.add(new Integer(root.val));
pathList.add(list);
}
}
else{
path.push(new Integer(root.val));
FindPath(root.left, target-root.val, path, pathList);
FindPath(root.right, target-root.val, path, pathList);
path.pop();
}
}
}
a. 該程式碼中使用了Stack來儲存當前路徑,更符合常規的邏輯。
b. 在儲存符合條件的路徑時,使用
for(int i:path){
list.add(new Integer(i));
}
是一個小技巧,避免了常規進出棧導致的複雜過程。
【情景模擬】
二叉樹root, t=22
其遍歷過程如下圖所示:
其中,t為該位置遍歷時,壓棧時候的值。箭頭的標號,為執行順序。