二叉樹路徑查找

最近筆試作了這麼一道題,想和你們分享一下個人作法

目錄:1.題目

   2.題目分析

   3.功能與模塊實現

   4.完整代碼

   5.總結

 

1、題目

二叉樹路徑查找

 

給定一棵二叉樹(結構以下),其中每一個節點值爲整數。給定一個值K,求全部知足以下條件的路徑並將路徑上節點的值打印出來:node

一、路徑方向必須向下,即只能從父節點指向子節點算法

二、路徑並非必須從根節點開始或在葉節點結束。即樹上任意節點開始向下走到任意節點的路徑都容許。數組

三、路徑上的節點得分之和等於給定值K。節點得分=節點值+節點所在層(根節點爲0,以後每層+1)。數據結構

 

l  示例:給定二叉樹[5,3,7,9,null,11,2,4,-1, null,null,2,-2],K=22ide

輸出:函數

5 3 9 -1ui

5 7 2 2spa

3 9 43d

解釋:如第一個路徑5 3 9 -1,路徑上節點得分分別爲5+0,3+1,9+2,-1+3,和爲22code

 

l  輸入格式: 第一行爲一個整數K,第二行爲一個二叉樹的層次遍歷序列,其中空子樹用 null 表示,每兩個數字或者null之間用空格分隔,例如:

22

5 3 7 9 null 11 2 4 -1 null null 2 -2

須要注意的是,null節點的子節點不會顯式的寫出來,如上例中第二行值爲3的節點的右子樹爲空,則該右空子樹的左右子樹不會再以null表示。

 

l  輸出格式: 分爲多行,每行爲一個知足條件的路徑上節點的值的序列,例如:

5 3 9 -1

5 7 2 2

3 9 4

 

 

現有以下輸入:

35

5 4 8 11 null 13 4 7 2 null null 5 1 8 null 7 10 6 null null null

請用程序將正確結果輸出

 

2、題目分析

 1.題目表面上想要查找出全部符合條件的路徑,其實更深層次考察的是,如何構造二叉樹

  2.如題目所述,二叉樹的層次遍歷序列是直接從中控臺輸入的,僅僅依靠此序列來層次構造二叉樹。這與咱們以往的說法不一樣,一般須要兩條序列(好比前序和中序)來構造二叉樹,或者構造徹底二叉樹的時候,能夠直接使用前序遍歷序列。

  3.題目還有一個要求,null節點的子節點不會顯式的寫出來,如上例中第二行值爲3的節點的右子樹爲空,則該右空子樹的左右子樹不會再以null表示。

  4.層次構造二叉樹過程:

  

 

  

 

 

 

   

 

 

 

    

  第五步和第六步中間紅色字段很重要,這是子節點找父節點的思路

  5.上面的分析是從上到下,按層次分析,經過父節點找其子節點,很好理解。可是代碼實現沒法作到這一點,咱們只能由子節點找其父節點,怎麼說呢?

  緣由是,父節點找其子節點,咱們一般會想到用遞歸,很是簡單,直接套用公式(n-NULLSUM)*2-x,這裏n表示父節點下標,x表示左右(左爲一,右爲二),一直找下去。可是仔細會發現,這裏的NULLSUM值可能不正確,

 

     

 

 

 

 

   在遞歸構造二叉樹的過程當中,沒法發作到層次構造,它更像前序遍歷構造形式。好比上圖,從根節點的右節點7開始,讀到11,此時11節點的下標是5,此時(5-NULLSUM)*2+1=11獲得左子節點2的下標,這裏NULLSUM爲何是0呢?11節點的前面有null節點啊,NULLSUM不該該是1嗎?咱們在看一下NULLSUM的定義:統計當前節點前面出現null值的節點。嗯咱們理解的定義沒有問題,問題出如今遞歸調用上,它不層次算法,而是一頭扎到底再回頭的那種,這就致使讀到11這個節點的時候,跳過了下標爲4的null節點,故NULLSUM值仍是爲0。

  6.基於以上問題,如何作到層次構造二叉樹呢?

  這裏我想到了一個辦法,那就是讓子節點找父節點,這樣作的好處是,一對一思想,找到父節點,就能夠跳到下一個節點繼續尋找其父節點。那麼還有個問題,怎麼知道當前節點是父節點的左節點仍是右節點?其實很簡單,能夠在第四點的層次構造二叉樹過程圖中能夠發現,每一個節點的左節點的下標必定是奇數,右節點的下標必定是偶數,那麼能夠根據當前節點的下標奇偶性判斷其是左節點仍是右節點。

  子節點找父節點,能夠經過(n-NULLSUM)*2-x這個公式的逆運算算出父節點的下標n,在利用樹的遍歷查詢,便可找到父節點

  7.不知道你們發現第六點又產生了個問題,那就是利用樹的遍歷查詢,由於從中控臺輸入一連串序列,這個序列中的數能夠不惟一,可重複,致使構造的樹每一個節點的值不惟一,那麼樹的遍歷就很差使了。如何解決這個問題呢?其實很簡單,既然節點的值不惟一,那咱們能夠在樹的數據結構裏,給節點增長一個下標變量用來標識該節點,好比:

 

 1 public class TreeNode {
 2     public int val;
 3     /*
 4      * 因爲題目給的二叉樹中節點值不惟一,
 5      * 增長treeIndex作惟一標識
 6      */
 7     public int treeIndex;//對應數組下標
 8     public TreeNode left;
 9     public TreeNode right;
10     //因爲數組爲String類型,須要轉型爲整型,方便後面運算
11     public TreeNode(String x) {
12         val = Integer.parseInt(x);
13     }
14     
15     public TreeNode() {
16         
17     }
18 
19     @Override
20     public String toString() {
21         return "TreeNode [val=" + val + "]";
22     }
23     
24     
25 }

 

 

 

 

 3、功能和模塊實現

  一、創建二叉樹

  1.一、尋找父節點

  

 1 public TreeNode searchNode(TreeNode root,int index) {//廣度優先搜索,查找父節點
 2         if(root==null||index<0)return null;
 3         LinkedList<TreeNode> list = new LinkedList<>();//鏈表,這裏我將其做爲隊列
 4         list.add(root);//把數據加入到隊列尾部
 5         while(!list.isEmpty()) {
 6             TreeNode node = list.poll();
 7             if(node.treeIndex==index) 
 8                 return node;
 9             if(node.left!=null)
10                 list.add(node.left);
11             if(node.right!=null)
12                 list.add(node.right);
13         }
14         
15         return null;
16     }

 

 

  1.二、處理傳入的序列

  

 1 public TreeNode create(String[] levelOrder) {//考慮到給的數組有null值,故用String類型
 2         if(levelOrder.length==0)
 3             return null;
 4         TreeNode root = new TreeNode(levelOrder[0]);//根節點
 5         LinkedList<Integer> list = new LinkedList<>();//鏈表,這裏我將其做爲隊列
 6         for(int i=1;i<levelOrder.length;i++) {
 7             if(levelOrder[i]==null||"null".equals(levelOrder[i])) {
 8                 list.add(i);
 9                 continue;
10             }
11             TreeNode node = new TreeNode(levelOrder[i]);
12             node.treeIndex = i;
13             
14             LinkedList<Integer> newList = new LinkedList();
15             for (Iterator iterator = list.iterator(); iterator.hasNext();) {
16                 newList.add((Integer) iterator.next());
17                 
18             }
19             buildTree(root,node,i,newList,levelOrder);
20             
21         }
22         return root;
23     }

  1.三、創建二叉樹

  

 1 //創建樹
 2     public TreeNode buildTree(TreeNode root,TreeNode node,int i,LinkedList<Integer> list,String[] levelOrder) {
 3         int NULLSUM = compareIndex(list,levelOrder,i);
 4         /*
 5          * 如題目給的示例:給定二叉樹[5,3,7,9,null,11,2,4,-1, null,null,2 ,-2]
 6          *                 index: 0,1,2,3,  4 ,5 ,6,7, 8,   9 , 10 ,11,12
 7          * 
 8          *                 5
 9          *                / \
10          *               3    7
11          *              /    / \
12          *          9    11  2
13          *         / \      / \
14          *        4   -1   2  -2
15          * 思路:1.定義NULLSUM變量記錄null節點個數
16          *        2.經過compareIndex函數計算該節點的父節點層及以上出現null節點個數
17          *        3.(i-2)/2+NULLSUM能夠計算出該節點的父節點
18          */
19         if(i%2==0) {
20             TreeNode parent = searchNode(root,(i-2)/2+NULLSUM);
21             while(parent==null) {
22                 NULLSUM++;
23                 parent = searchNode(root,(i-2)/2+NULLSUM);
24             }
25             parent.right = node;
26         }else {
27             TreeNode parent = searchNode(root,(i-1)/2+NULLSUM);
28             while(parent==null) {
29                 NULLSUM++;
30                 parent = searchNode(root,(i-1)/2+NULLSUM);
31             }
32             parent.left = node;
33         }
34         
35         return root;
36     }

  爲何在第21和28行設立while循環判斷找到的父節點是否爲null?還記得下面這張圖嗎,父節點爲null時,就會跳到下一個不爲null的節點來代替指向本來父節點的子節點。逆過來,子節點找父節點,若是父節點時null,那能夠跳到下一個直至不爲null的父節點

  二、二叉樹路徑查找

  2.一、路徑查找入口

  

1 /*
2      * 傳入的參數分別爲根節點root、定值K和當前節點所在層數dept(這個很是好用,由於傳入的根節點有可能只是樹中某節點)
3      */
4     public List<List<Integer>> pathSumEntry(TreeNode root,int K,int dept){
5         List<List<Integer>> result = new LinkedList<List<Integer>>();//用於保存全部匹配路徑
6         List<Integer> currentResult = new LinkedList<Integer>();//用於保存找到的當前匹配的路徑
7         pathSum(root,K,currentResult,result,dept);
8         return result;
9     }

  2.二、路徑查找主函數

 1 /*
 2      * 這裏主要使用遞歸加回溯的思想
 3      */
 4     public void pathSum(TreeNode root,int K,List<Integer>currentResult,List<List<Integer>>result,int dept) {
 5         if(root==null)return;
 6         currentResult.add(new Integer(root.val));
 7         if(root.left==null&&root.right==null&&K==root.val+dept) {
 8             result.add(new LinkedList(currentResult));
 9         }else {
10             pathSum(root.left,K-root.val-dept,currentResult,result,dept+1);
11             pathSum(root.right,K-root.val-dept,currentResult,result,dept+1);
12         }
13         currentResult.remove(currentResult.size()-1);
14     }

 

4、完整代碼

  1.樹的數據結構

 1 public class TreeNode {
 2     public int val;
 3     /*
 4      * 因爲題目給的二叉樹中節點值不惟一,
 5      * 增長treeIndex作惟一標識
 6      */
 7     public int treeIndex;//對應數組下標
 8     public TreeNode left;
 9     public TreeNode right;
10     //因爲數組爲String類型,須要轉型爲整型,方便後面運算
11     public TreeNode(String x) {
12         val = Integer.parseInt(x);
13     }
14     
15     public TreeNode() {
16         
17     }
18 
19     @Override
20     public String toString() {
21         return "TreeNode [val=" + val + "]";
22     }
23     
24     
25 }
View Code

  2.構造二叉樹類

  1 public class BuildTree {
  2     
  3     public TreeNode searchNode(TreeNode root,int index) {//廣度優先搜索,查找父節點
  4         if(root==null||index<0)return null;
  5         LinkedList<TreeNode> list = new LinkedList<>();//鏈表,這裏我將其做爲隊列
  6         list.add(root);//把數據加入到隊列尾部
  7         while(!list.isEmpty()) {
  8             TreeNode node = list.poll();
  9             if(node.treeIndex==index) 
 10                 return node;
 11             if(node.left!=null)
 12                 list.add(node.left);
 13             if(node.right!=null)
 14                 list.add(node.right);
 15         }
 16         
 17         return null;
 18     }
 19     
 20     
 21     public TreeNode create(String[] levelOrder) {//考慮到給的數組有null值,故用String類型
 22         if(levelOrder.length==0)
 23             return null;
 24         TreeNode root = new TreeNode(levelOrder[0]);//根節點
 25         LinkedList<Integer> list = new LinkedList<>();//鏈表,這裏我將其做爲隊列
 26         for(int i=1;i<levelOrder.length;i++) {
 27             if(levelOrder[i]==null||"null".equals(levelOrder[i])) {
 28                 list.add(i);
 29                 continue;
 30             }
 31             TreeNode node = new TreeNode(levelOrder[i]);
 32             node.treeIndex = i;
 33             
 34             LinkedList<Integer> newList = new LinkedList();
 35             for (Iterator iterator = list.iterator(); iterator.hasNext();) {
 36                 newList.add((Integer) iterator.next());
 37                 
 38             }
 39             buildTree(root,node,i,newList,levelOrder);
 40             
 41         }
 42         return root;
 43     }
 44 
 45 
 46     //創建樹
 47     public TreeNode buildTree(TreeNode root,TreeNode node,int i,LinkedList<Integer> list,String[] levelOrder) {
 48         int NULLSUM = compareIndex(list,levelOrder,i);
 49         /*
 50          * 如題目給的示例:給定二叉樹[5,3,7,9,null,11,2,4,-1, null,null,2 ,-2]
 51          *                 index: 0,1,2,3,  4 ,5 ,6,7, 8,   9 , 10 ,11,12
 52          * 
 53          *                 5
 54          *                / \
 55          *               3    7
 56          *              /    / \
 57          *          9    11  2
 58          *         / \      / \
 59          *        4   -1   2  -2
 60          * 思路:1.定義NULLSUM變量記錄null節點個數
 61          *        2.經過compareIndex函數計算該節點的父節點層及以上出現null節點個數
 62          *        3.(i-2)/2+NULLSUM能夠計算出該節點的父節點
 63          */
 64         if(i%2==0) {
 65             TreeNode parent = searchNode(root,(i-2)/2+NULLSUM);
 66             while(parent==null) {
 67                 NULLSUM++;
 68                 parent = searchNode(root,(i-2)/2+NULLSUM);
 69             }
 70             parent.right = node;
 71         }else {
 72             TreeNode parent = searchNode(root,(i-1)/2+NULLSUM);
 73             while(parent==null) {
 74                 NULLSUM++;
 75                 parent = searchNode(root,(i-1)/2+NULLSUM);
 76             }
 77             parent.left = node;
 78         }
 79         
 80         return root;
 81     }
 82     
 83     /*
 84      * 比較下標所指向的值,判斷當前節點的父節點下標所在數組位置的值是否等於null
 85      * list爲存儲空值的下標隊列,從頭至尾取值,並計算比較當前節點下標比棧值的子節點大
 86      * 若大於,這NULLSUM++,不然中止,返回NULLSUM值
 87      */
 88     public int compareIndex(LinkedList<Integer> list,String[] order,int i) {
 89         int sum,NULLSUM = 0;//記錄數組中null的數量
 90         if(list==null&&list.size()==0) {
 91             return 0;
 92         }
 93         while(!list.isEmpty()&&i>list.poll()*2) {
 94             NULLSUM++;
 95         }
 96         return NULLSUM;
 97     }
 98     
 99     
100 }
View Code

  3.路徑查找類

 1 public class PathSum {
 2 
 3     /*
 4      * 傳入的參數分別爲根節點root、定值K和當前節點所在層數dept(這個很是好用,由於傳入的根節點有可能只是樹中某節點)
 5      */
 6     public List<List<Integer>> pathSumEntry(TreeNode root,int K,int dept){
 7         List<List<Integer>> result = new LinkedList<List<Integer>>();//用於保存全部匹配路徑
 8         List<Integer> currentResult = new LinkedList<Integer>();//用於保存找到的當前匹配的路徑
 9         pathSum(root,K,currentResult,result,dept);
10         return result;
11     }
12     /*
13      * 這裏主要使用遞歸加回溯的思想
14      */
15     public void pathSum(TreeNode root,int K,List<Integer>currentResult,List<List<Integer>>result,int dept) {
16         if(root==null)return;
17         currentResult.add(new Integer(root.val));
18         if(root.left==null&&root.right==null&&K==root.val+dept) {
19             result.add(new LinkedList(currentResult));
20         }else {
21             pathSum(root.left,K-root.val-dept,currentResult,result,dept+1);
22             pathSum(root.right,K-root.val-dept,currentResult,result,dept+1);
23         }
24         currentResult.remove(currentResult.size()-1);
25     }
26 }
View Code

  4.主函數

 1 public class Main {
 2     
 3     public static void main(String[] args) {
 4         Scanner input = new Scanner(System.in);
 5         System.out.print("輸入K:");
 6         int K = input.nextInt();
 7         System.out.println("輸入數組(以'#'結束,例如:5 3 7 9 null 11 2 4 -1 null null 2 -2 #)");
 8         List<String> list = new ArrayList<String>();//集合接收輸入串
 9         String cin = null;
10         while(!"#".equals((cin=input.next()))) {
11             list.add(cin);
12             
13         }
14         //將集合轉成字符串數組
15         String[] levelOrder = new String[list.size()];
16         for(int i=0;i<=list.size()-1;i++) {
17             levelOrder[i] = list.get(i);
18         }
19         
20         //從左到右構造二叉樹,並尋找路徑和等於K的路徑
21         BuildTree tree = new BuildTree();
22         TreeNode root = tree.create(levelOrder);
23         printTree(root,K,0);
24     }
25     
26     public static void printTree(TreeNode root,int K,int dept) {
27         if(root==null)return;
28         PathSum pathSum = new PathSum();
29 
30         List<List<Integer>> result = pathSum.pathSumEntry(root, K,dept);
31         for (List resultList : result) {
32             System.out.println(resultList);
33         }
34         
35         printTree(root.left,K,dept+1);
36         printTree(root.right,K,dept+1);
37     }
38 }
View Code

 

 

5、總結

  一開始我還覺得這道題就是個簡單的路徑查找算題,採用遞歸加回溯算法分分鐘鍾就能夠解決這道題,當我再仔細看題的時候,才知道這不是一道簡簡單單的回溯算法題。還有二叉樹如何構建問題,一看到構建二叉樹題,仍是以爲簡單,結果本身仍是太嫩了!沒錯,這是筆者第一次作分層構造二叉樹題,筆者的思考能夠參考題目分析部分,這裏有筆者的思考的部分過程。第一次作分層構造二叉樹,因此花費了很多時間,考慮不周還望各位老師同窗指點,若是你們有更好的分層構造二叉樹想法,能夠分享連接到評論區,好比能夠直接父節點找子節點。

  每日一囧,微笑面對生活,我是懂先森

相關文章
相關標籤/搜索