T1是一棵含有幾百萬個節點的樹,T2含有幾百個節點。判斷T2是不是T1 的子樹。算法
首先考慮小數據量的狀況,能夠根據樹的前序和中序遍歷所得的字符串,來經過判斷T2生成的字符串是不是T1字符串的子串,來判斷T2是不是T1的子樹。假設T1的節點數爲N,T2的節點數爲M。遍歷兩棵樹算法時間複雜性是O(N + M), 判斷字符串是否爲另外一個字符串的子串的複雜性也是O( N + M)(好比使用KMP算法)。所須要的空間也是O(N + M)。scala
這裏有一個問題須要注意:對於左節點或者右節點爲null的狀況,須要在字符串中插入特殊字符表示。不然對於下面這種情形將會判斷錯誤:code
所以若是插入特殊字符,上述兩棵樹的中序和前序遍歷的結果是相同的。blog
因爲本例有幾百萬的節點,須要佔用O(N + M)的內存。遞歸
若是換一種思路,就是遍歷T1,每當T1的某個節點與T2的根節點值相同時,就判斷兩棵子樹是否相同。這個算法的複雜度是O(N*M)。咱們再仔細思考一下。由於只有在節點值與T2的根節點值相同纔會調用O(M)。假設有K次這種狀況,那麼算法的複雜度就是O(N + K*M)。下面是代碼實現:ip
struct TreeNode{ TreeNode *leftChild; TreeNode *rightChild; int data; }; // check sub tree n1 == sub tree n2 bool checkSubTree(const TreeNode* n1, const TreeNode* n2){ if( n1 == nullptr && n2 == nullptr ) return true; if( n1 == nullptr || n2 == nullptr ) return false; if( n1->data != n2->data ) return false; return checkSubTree(n1->leftChild, n2->leftChild) && checkSubTree(n1->rightChild, n2->rightChild); } bool subTree(const TreeNode *n1, const TreeNode *n2){ if( n1 == nullptr){ return false; // the bigger tree is empty, so t2 is not subtree of t1 } if( n1->data == n2->data){ if( checkSubTree(n1, n2)) return true; } return subTree(n1->leftChild, n2) || subTree(n2->rightChild, n2); }
1)方法一會佔用O(N + M)的內存,而另一種解法只會佔用O(logN + logM)的內存(遞歸的棧內存)。當考慮scalability擴展性時,內存使用的多寡是個很重要的因素。內存
2)方法一的時間複雜度爲O(N + M),方法二最差的時間複雜度是O(N*M)。因此要經過工程實踐或者是歷史數據看一下哪一種方法更優。固然了,方法二也可能會很早發現兩棵樹的不一樣,早早的退出了checkSubTree。字符串
總的來講,在空間使用上,方法二更好。在時間上,須要經過實際數據來驗證。
it