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


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

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

題目 

  輸入一棵二叉樹和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。

思路

  1.假設找到了其中一條路徑,達到葉結點后,由於沒有指向父結點的指針,所以必須提前創建一個鏈表存儲前面經過的結點。

  2.由於是從根結點出發,所以要想到使用前序遍歷

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

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

  改進:書中的代碼是根據所有結點的和是否等於輸入的整數,判斷是否打印輸出。其實沒有這個必要,只需要在每次遍歷到一個結點時,令目標整數等於自己減去當前結點的值,若到達根結點時,最終的目標整數等於0就可以打印輸出。(描述得不是很清楚,就是相當於每個結點的目標整數不同,詳見代碼)

測試算例 

  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實現合集  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM