【Java】 劍指offer(34) 二叉樹中和爲某一值的路徑

本文參考自《劍指offer》一書,代碼採用Java語言。html

更多:《劍指Offer》Java實現合集  java

題目 

  輸入一棵二叉樹和一個整數,打印出二叉樹中結點值的和爲輸入整數的全部路徑。從樹的根結點開始往下一直到葉結點所通過的結點造成一條路徑。node

思路

  1.假設找到了其中一條路徑,達到葉結點後,因爲沒有指向父結點的指針,因此必須提早建立一個鏈表存儲前面通過的結點。函數

  2.因爲是從根結點出發,因此要想到使用前序遍歷post

  3.利用鏈表存儲結點,在該結點完成左右子樹的路徑搜索後(即遞歸函數結束,返回到其父結點以前),要刪除該結點,從而記錄別的路徑。測試

  具體實現:經過前序遍歷,從根結點出發,每次在鏈表中存儲遍歷到的結點,若到達葉子結點,則根據全部結點的和是否等於輸入的整數,判斷是否打印輸出。在當前結點訪問結束後,遞歸函數將會返回到它的父結點,因此在函數退出以前,要刪除鏈表中的當前結點,以確保返回父結點時,儲存的路徑恰好是從根結點到父結點。this

  改進:書中的代碼是根據全部結點的和是否等於輸入的整數,判斷是否打印輸出。其實沒有這個必要,只須要在每次遍歷到一個結點時,令目標整數等於本身減去當前結點的值,若到達根結點時,最終的目標整數等於0就能夠打印輸出。(描述得不是很清楚,就是至關於每一個結點的目標整數不一樣,詳見代碼)url

測試算例 spa

  1.功能測試(一條或者多條對應路徑;無對應路徑;結點值爲正負零;)指針

  2.特殊測試(根結點爲null)

Java代碼

//題目:輸入一棵二叉樹和一個整數,打印出二叉樹中結點值的和爲輸入整數的所
//有路徑。從樹的根結點開始往下一直到葉結點所通過的結點造成一條路徑。

public class PathInTree {
	public class TreeNode {
	    int val = 0;
	    TreeNode left = null;
	    TreeNode right = null;
	    public TreeNode(int val) {
	        this.val = val;
	    }
	}
	
	public void findPath(TreeNode root,int target) {
		if(root==null)
			return;
		ArrayList<Integer> list= new ArrayList<>();
		printPath(root, target,list);
	}
	
	private void printPath(TreeNode node,int target,ArrayList<Integer> list) {
		if(node==null)
			return;
		list.add(node.val);
		target-=node.val;  //每一個結點的target不會受到方法的影響而改變
		if(target==0 && node.left==null && node.right==null) {  //葉子結點
				for (Integer integer : list)
					System.out.print(integer+" ");
				System.out.println();
		}else {		//中間結點
			printPath(node.left, target, list);
			printPath(node.right, target, list);
		}
		list.remove(list.size()-1);
	}

  

  牛客網中的題目有多兩點要求:1.返回類型爲ArrayList<ArrayList<Integer>>,即返回全部路徑的集合;2.要求返回的集合中,長度大的靠前。下面是實現的代碼:

/*
 * 幾個要點:
 * 1. 將nodeList和pathList定義成全局變量,避免在方法中的屢次傳遞
 * 2. 在pathList中添加nodeList時,由於nodeList會不斷變化,因此必須新建一個list存入
 *    複製ArrayList的方法:newList=new ArrayList<Integer>(oldList)(複製內容,而不是複製地址,注意與newList=oldList的區分)
 * 3. 在當前結點完成左右子樹的路徑搜索後,記得刪除nodeList中的當前結點
 * 4. target是基本數據類型int,不會受到方法的影響而改變
 */
private ArrayList<Integer> nodeList= new ArrayList<>();
private ArrayList<ArrayList<Integer>> pathList = new ArrayList<>();
 
public ArrayList<ArrayList<Integer>> FindPath(TreeNode node,int target) {
    if(node==null)
        return pathList;
    nodeList.add(node.val);
    target-=node.val;
    if(target==0 && node.left==null && node.right==null) {  //葉子結點
        //長度大的nodeList插入到pathList的前面
        int i=0;
        while(i<pathList.size() && nodeList.size()<pathList.get(i).size() ) //記得防止越界
                i++;
        pathList.add(i, new ArrayList<Integer>(nodeList));  //nodeList會隨方法變化,必須新建一個list存入pathList中!
    }else {  //不是葉子結點
        pathList=FindPath(node.left, target);
        pathList=FindPath(node.right, target);
    }
    nodeList.remove(nodeList.size()-1);  //記得刪除當前結點
    return pathList;
}

  

收穫

  1.二叉樹的許多題目與遍歷(包括層序遍歷)有關,要深入理解;根據根結點的位置判斷使用哪種遍歷。

  2.二叉樹遍歷過程沒有父結點指針,要保存路徑的話,必需要建立容器存儲以前的結點。

  3.熟悉這道題中在每次遞歸函數結束前刪除當前結點的操做,這能夠確保返回到父結點時路徑恰好是從根結點到父結點。

  4.target-=node.val這句代碼很是好,多多體會。

  5.牛客網的那部分代碼:在鏈表中存儲一個對象時,若是該對象是不斷變化的,則應該建立一個新的對象複製該對象的內容(而不是指向同一個對象),將這個新的對象存儲到鏈表中。。若是直接存儲該對象的話,鏈表中的對象也會不斷變化。基本數據類型和String則沒有這種問題。說到底實際上是存儲的是地址仍是值的問題。這篇文章討論了一下。

 

更多:《劍指Offer》Java實現合集 

相關文章
相關標籤/搜索