海量數據:判斷一棵樹是否爲另外一棵樹的子樹

     T1是一棵含有幾百萬個節點的樹,T2含有幾百個節點。判斷T2是不是T1 的子樹。算法

首先考慮小數據量的狀況,能夠根據樹的前序和中序遍歷所得的字符串,來經過判斷T2生成的字符串是不是T1字符串的子串,來判斷T2是不是T1的子樹。假設T1的節點數爲N,T2的節點數爲M。遍歷兩棵樹算法時間複雜性是O(N + M), 判斷字符串是否爲另外一個字符串的子串的複雜性也是O( N + M)(好比使用KMP算法)。所須要的空間也是O(N + M)。scala

這裏有一個問題須要注意:對於左節點或者右節點爲null的狀況,須要在字符串中插入特殊字符表示。不然對於下面這種情形將會判斷錯誤:遞歸

所以若是插入特殊字符,上述兩棵樹的中序和前序遍歷的結果是相同的。內存

因爲本例有幾百萬的節點,須要佔用O(N + M)的內存。字符串

若是換一種思路,就是遍歷T1,每當T1的某個節點與T2的根節點值相同時,就判斷兩棵子樹是否相同。這個算法的複雜度是O(N*M)。咱們再仔細 思考一下。由於只有在節點值與T2的根節點值相同纔會調用O(M)。假設有K次這種狀況,那麼算法的複雜度就是O(N + K*M)。get

  1. package Tree_Graph;  
  2.   
  3. import CtCILibrary.AssortedMethods;  
  4. import CtCILibrary.TreeNode;  
  5.   
  6. public class S4_8 {  
  7.   
  8.     // 判斷t2是不是t1的子樹  
  9.     public static boolean isSubTree(TreeNode t1, TreeNode t2) {  
  10.         if(t2 == null) {            // 空樹始終是另外一個樹的子樹  
  11.             return true;  
  12.         }  
  13.         if(t1 == null) {        // 此時r2非空,非空樹不多是一個空樹的子樹  
  14.             return false;  
  15.         }  
  16.         if(t1.data == t2.data) {            // 找到兩樹匹配  
  17.             if(isSameTree(t1, t2)){  
  18.                 return true;  
  19.             }  
  20.         }  
  21.           
  22.         // 繼續在r1的左子樹和右子樹裏找匹配  
  23.         return isSubTree(t1.left, t2) || isSubTree(t1.right, t2);  
  24.     }  
  25.       
  26.     // 判斷兩棵樹是否相同  
  27.     public static boolean isSameTree(TreeNode t1, TreeNode t2) {  
  28.         if(t1==null && t2==null) {  
  29.             return true;  
  30.         }  
  31.         if(t1==null || t2==null) {  
  32.             return false;  
  33.         }  
  34.           
  35.         if(t1.data != t2.data) {  
  36.             return false;  
  37.         }  
  38.         return isSameTree(t1.left, t2.left) && isSameTree(t1.right, t2.right);  
  39.     }  
  40.       
  41.     public static void main(String[] args) {  
  42.         // t2 is a subtree of t1  
  43.         int[] array1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};  
  44.         int[] array2 = {2, 4, 5, 8, 9, 10, 11};  
  45.   
  46.         TreeNode t1 = AssortedMethods.createTreeFromArray(array1);  
  47.         TreeNode t2 = AssortedMethods.createTreeFromArray(array2);  
  48.   
  49.         if (isSubTree(t1, t2))  
  50.             System.out.println("t2 is a subtree of t1");  
  51.         else  
  52.             System.out.println("t2 is not a subtree of t1");  
  53.   
  54.         // t4 is not a subtree of t3  
  55.         int[] array3 = {1, 2, 3};  
  56.         TreeNode t3 = AssortedMethods.createTreeFromArray(array1);  
  57.         TreeNode t4 = AssortedMethods.createTreeFromArray(array3);  
  58.   
  59.         if (isSubTree(t3, t4))  
  60.             System.out.println("t4 is a subtree of t3");  
  61.         else  
  62.             System.out.println("t4 is not a subtree of t3");  
  63.     }  
  64. }

 

 

 

對於上面討論的2種解法,哪一種解法比較好呢?其實有必要好好討論一番:it

1)方法一會佔用O(N + M)的內存,而另一種解法只會佔用O(logN + logM)的內存(遞歸的棧內存)。當考慮scalability擴展性時,內存使用的多寡是個很重要的因素。class

2)方法一的時間複雜度爲O(N + M),方法二最差的時間複雜度是O(N*M)。因此要經過工程實踐或者是歷史數據看一下哪一種方法更優。固然了,方法二也可能會很早發現兩棵樹的不一樣,早早的退出了checkSubTree。import

總的來講,在空間使用上,方法二更好。在時間上,須要經過實際數據來驗證。擴展

相關文章
相關標籤/搜索