給定一棵二叉樹(結構以下),其中每一個節點值爲整數。給定一個值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
請用程序將正確結果輸出
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 }
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 }
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 }
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 }
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 }
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 }
一開始我還覺得這道題就是個簡單的路徑查找算題,採用遞歸加回溯算法分分鐘鍾就能夠解決這道題,當我再仔細看題的時候,才知道這不是一道簡簡單單的回溯算法題。還有二叉樹如何構建問題,一看到構建二叉樹題,仍是以爲簡單,結果本身仍是太嫩了!沒錯,這是筆者第一次作分層構造二叉樹題,筆者的思考能夠參考題目分析部分,這裏有筆者的思考的部分過程。第一次作分層構造二叉樹,因此花費了很多時間,考慮不周還望各位老師同窗指點,若是你們有更好的分層構造二叉樹想法,能夠分享連接到評論區,好比能夠直接父節點找子節點。
每日一囧,微笑面對生活,我是懂先森