一,問題描述
構建一棵二叉樹(不一定是二叉查找樹),求出該二叉樹中第K層中的結點個數(根結點為第0層)
二,二叉樹的構建
定義一個BinaryTree類來表示二叉樹,二叉樹BinaryTree 又是由各個結點組成的,因此需要定義一個結點類BinaryNode,BinaryNode作為BinaryTree的內部類。
此外,在BinaryTree中需要一定一個BinaryNode屬性來表示樹的根結點。
1 public class BinaryTree<T extends Comparable<? super T>> { 2 3 private static class BinaryNode<T>{ 4 T element; 5 BinaryNode<T> left; 6 BinaryNode<T> right; 7 8 public BinaryNode(T element) { 9 this.element = element; 10 left = right = null; 11 } 12 13 public BinaryNode(T element, BinaryNode<T> left, BinaryNode<T> right){ 14 this.element = element; 15 this.left = left; 16 this.right = right; 17 } 18 } 19 20 private BinaryNode<T> root; 21 22 //other code.....
第一行是二叉樹類的定義,第三行是結點類的定義,第20行是二叉樹根的定義。
三,求解第K層結點個數的算法實現
感覺對二叉樹中的許多操作都可以用遞歸來實現。因此,二叉樹是理解遞歸一個好實例。比如,二叉樹的操作之統計二叉樹中節點的個數 和 二叉樹的先序遍歷和后序遍歷的應用--輸出文件和統計目錄大小
求第K層結點的個數也可以用遞歸來實現:
①若二叉樹為空或者K小於0,返回0
②若K等於0,第0層就是樹根,根只有一個,返回1
③若K大於0,返回左子樹中第K-1層結點個數 加上 右子樹中第K-1層結點的個數
因為,第K層結點,相對於根的左子樹 和 右子樹 而言,就是第K-1層結點
其實,這是有改進的地方:對於K<0的情形,准確地說:它只是一個非法輸入,而不是遞歸的結束條件(基准條件)。可以看出,①不要把非法輸入與遞歸的基准條件混淆,②把非法輸入的判斷放到遞歸中判斷的開銷是很大的。因為每進行一次遞歸就需要進行一次非法輸入判斷。而如果在開始就把非法輸入過濾掉,在遞歸過程中就不會存在每一次遞歸就判斷一次非法輸入了。
遞歸的基准條件只有兩個:
1) k==0 當遞歸到K==0時,說明:第K層是有結點的
2) root==null 當遞歸到root==null時,說明:第K層沒有結點
因此,可以進一步將代碼改進如下:這樣,不需要在每次遞歸的過程中還可能附加一次 k<0 的判斷
1 /** 2 * 3 * @param k 4 * @return 二叉樹中第K層結點的個數(根位於第0層) 5 */ 6 public int k_nodes(int k){ 7 if(k < 0) 8 return 0; 9 return k_nodes(root, k); 10 } 11 private int k_nodes(BinaryNode<T> root, int k){ 12 if(root == null) 13 return 0; 14 if(k == 0) 15 return 1;//根結點 16 else 17 return k_nodes(root.left, k-1) + k_nodes(root.right, k-1); 18 }
可參考:按層打印二叉樹--每行打印一層 來測試每一層是否有正確的結點個數。
四,代碼實現
1 public class BinaryTree<T extends Comparable<? super T>> { 2 3 private static class BinaryNode<T>{ 4 T element; 5 BinaryNode<T> left; 6 BinaryNode<T> right; 7 8 public BinaryNode(T element) { 9 this.element = element; 10 left = right = null; 11 } 12 } 13 14 private BinaryNode<T> root; 15 16 /** 17 * 向二叉樹中插入一個元素 18 * @param element 19 */ 20 public void insert(T element){ 21 root = insert(root, element); 22 } 23 private BinaryNode<T> insert(BinaryNode<T> root, T element){ 24 if(root == null) 25 return new BinaryNode<T>(element); 26 int r = (int)(2*Math.random()); 27 //隨機地將元素插入到左子樹 或者 右子樹中 28 if(r==0) 29 root.left = insert(root.left, element); 30 else 31 root.right = insert(root.right, element); 32 return root; 33 } 34 35 /** 36 * 37 * @param k 38 * @return 二叉樹中第K層結點的個數(根位於第0層) 39 */ 40 public int k_nodes(int k){ 41 return k_nodes(root, k); 42 } 43 private int k_nodes(BinaryNode<T> root, int k){ 44 if(root == null || k < 0) 45 return 0; 46 if(k == 0) 47 return 1;//根結點 48 else 49 return k_nodes(root.left, k-1) + k_nodes(root.right, k-1); 50 } 51 52 public static void main(String[] args) { 53 BinaryTree<Integer> tree = new BinaryTree<>(); 54 55 int[] ele = C2_2_8.algorithm1(4);//構造一個隨機數組,數組元素的范圍為[1,4] 56 for (int i = 0; i < ele.length; i++) { 57 tree.insert(ele[i]); 58 } 59 60 int k_nodes = tree.k_nodes(2);//第二層 61 int k_nodes2 = tree.k_nodes(-1);//第-1層 62 int k_nodes3 = tree.k_nodes(0); 63 int k_nodes4 = tree.k_nodes(1); 64 int k_nodes5 = tree.k_nodes(4);//若超過了樹的高度,結果為0 65 System.out.println(k_nodes); 66 System.out.println(k_nodes2); 67 System.out.println(k_nodes3); 68 System.out.println(k_nodes4); 69 System.out.println(k_nodes5); 70 } 71 }
關於 C2_2_8類,參考:隨機序列生成算法---生成前N個整數的一組隨機序列
五,參考資料
http://blog.csdn.net/luckyxiaoqiang/article/details/7518888