n个结点,不同形态的二叉树(数目+生成)
題目鏈接:
不同的二叉查找樹:http://www.lintcode.com/zh-cn/problem/unique-binary-search-trees/
不同的二叉查找樹 II:http://www.lintcode.com/zh-cn/problem/unique-binary-search-trees-ii/
不同形態(tài)二叉樹的數(shù)目:
樣例
給出n = 3,有5種不同形態(tài)的二叉查找樹:
1 3 3 2 1\ / / / \ \3 2 1 1 3 2/ / \ \2 1 2 3分析
?可以分析,當(dāng)n=1時,只有1個根節(jié)點(diǎn),則只能組成1種形態(tài)的二叉樹,令n個節(jié)點(diǎn)可組成的二叉樹數(shù)量表示為h(n),則h(1)=1; h(0)=0;
?????? 當(dāng)n=2時,1個根節(jié)點(diǎn)固定,還有2-1個節(jié)點(diǎn)。這一個節(jié)點(diǎn)可以分成(1,0),(0,1)兩組。即左邊放1個,右邊放0個;或者左邊放0個,右邊放1個。即:h(2)=h(0)*h(1)+h(1)*h(0)=2,則能組成2種形態(tài)的二叉樹。
????? 當(dāng)n=3時,1個根節(jié)點(diǎn)固定,還有2個節(jié)點(diǎn)。這2個節(jié)點(diǎn)可以分成(2,0),(1,1),(0,2)3組。即h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=5,則能組成5種形態(tài)的二叉樹。
以此類推,當(dāng)n>=2時,可組成的二叉樹數(shù)量為h(n)=h(0)*h(n-1)+h(1)*h(n-2)+...+h(n-1)*h(0)種,即符合Catalan數(shù)的定義,可直接利用通項公式得出結(jié)果。
令h(1)=1,h(0)=1,catalan數(shù)(卡特蘭數(shù))滿足遞歸式:?
h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0) (其中n>=2)
另類遞歸式:
h(n)=((4*n-2)/(n+1))*h(n-1);該遞推關(guān)系的解為:
h(n)=C(2n,n)/(n+1) (n=1,2,3,...)
由此想到了上次說的"N個數(shù)依次入棧,出棧順序有多少種?", ?同樣用的也是卡特蘭數(shù)。
?http://www.cnblogs.com/hujunzheng/p/4845354.html
代碼
class Solution { public:/*** @paramn n: An integer* @return: An integer*/long long C(int n, int m){n = n-m+1;long long ans = 1;for(int i=1; i<=m; ++i){ans *= n++;ans /= i;}return ans;}int numTrees(int n) {// write your code herereturn C(2*n, n)/(n+1);} };?
構(gòu)建不同形態(tài)二叉樹:
樣例
給出n = 3,生成所有5種不同形態(tài)的二叉查找樹:
1 3 3 2 1\ / / / \ \3 2 1 1 3 2/ / \ \2 1 2 3其實(shí)通過樣例,我們可以發(fā)現(xiàn)n個結(jié)點(diǎn)構(gòu)造不同形態(tài)二叉樹的過程,1,2,3.....n個結(jié)點(diǎn),枚舉每一個結(jié)點(diǎn)為根結(jié)點(diǎn)(假設(shè)為root, 1<=root<=n), 那么(1,2..root-1)和(root+1, root+2...n)分別是root的左右子樹。每一步不斷地重復(fù)上述過程,最終會得到所有形態(tài)的二叉樹。
算法實(shí)現(xiàn)
先弱弱的說一下自己錯誤的實(shí)現(xiàn),因?yàn)檫f歸實(shí)現(xiàn)的時候會得到不同的二叉樹,那么如何判斷n個結(jié)點(diǎn)正好生成了二叉樹呢?于是用了一個變量useNode(=0),表示當(dāng)前已經(jīng)用了多少個結(jié)點(diǎn)建樹。當(dāng)useNode等于n的時候說明產(chǎn)生了一棵符合要求的樹,接著拷貝一下剛才生成的樹,然后放入vector中,繼續(xù)建造下一棵符合條件的二叉樹。
錯誤代碼:
/*** Definition of TreeNode:* class TreeNode {* public:* int val;* TreeNode *left, *right;* TreeNode(int val) {* this->val = val;* this->left = this->right = NULL;* }* }*/ class Solution { public:/*** @paramn n: An integer* @return: A list of root*/vector<TreeNode *> ans;int cntNode=0;//節(jié)點(diǎn)的總數(shù)TreeNode *curRoot = NULL;void copyT(TreeNode * &tmp, TreeNode *T){if(T){tmp = new TreeNode(T->val);copyT(tmp->left, T->left);copyT(tmp->right, T->right);}}void buildT(TreeNode * &T, int ld, int rd, int useNode){if(ld > rd) return;for(int root=ld; root<=rd; ++root){T = new TreeNode(root);if(ld==1 && rd==cntNode)curRoot = T;if(useNode+1==cntNode){//這個樹已經(jīng)建立完畢,拷貝一下吧TreeNode *tmp = NULL;copyT(tmp, curRoot);ans.push_back(tmp);}buildT(T->left, ld, root-1, useNode+1);buildT(T->right, root+1, rd, useNode+root-ld+1);}}vector<TreeNode *> generateTrees(int n) {// write your code herecntNode = n;TreeNode *T = NULL;buildT(T, 1, n, 0);if(n == 0) ans.push_back(T);return ans;} };后來運(yùn)行之后,看到錯誤的答案與正確答案的對比,如下:
當(dāng)n=4的時候輸出
[{1,#,2,#,3,#,4},{1,#,2,#,4,3},{1,#,3,2,4},{1,#,4,2,#,#,3},{1,#,4,3,#,2},{2,1,3,#,#,#,4},{2,1,4,#,#,3},{3,2,4,1},{4,1,#,#,2,#,3},{4,1,#,#,3,2},{4,2,#,1,3},{4,3,#,1,#,#,2},{4,3,#,2,#,1}]期望答案
[{1,#,2,#,3,#,4},{1,#,2,#,4,3},{1,#,3,2,4},{1,#,4,2,#,#,3},{1,#,4,3,#,2},{2,1,3,#,#,#,4},{2,1,4,#,#,3},{3,1,4,#,2},{3,2,4,1},{4,1,#,#,2,#,3},{4,1,#,#,3,2},{4,2,#,1,3},{4,3,#,1,#,#,2},{4,3,#,2,#,1}]也就是少了{(lán)3,1,4,#,2},以3為根結(jié)點(diǎn)的二叉樹為什么會少了呢?仔細(xì)想想,3結(jié)點(diǎn)的左孩子可以是1,也可以是2,那么左孩子為1的情況就被忽略了,此時useNode并不等于n,然后就換成左孩子為2結(jié)點(diǎn)的情況了。
正確代碼:
/*** Definition of TreeNode:* class TreeNode {* public:* int val;* TreeNode *left, *right;* TreeNode(int val) {* this->val = val;* this->left = this->right = NULL;* }* }*/ class Solution { public:/*** @paramn n: An integer* @return: A list of root*/vector<TreeNode *> buildT(int ld, int rd){vector<TreeNode *> ans;if(ld == rd) {TreeNode *T = new TreeNode(ld);ans.push_back(T);return ans;}if(ld > rd){ans.push_back(NULL);return ans;}for(int i=ld; i<=rd; ++i){vector<TreeNode *> ansLeft = buildT(ld, i-1);vector<TreeNode *> ansRight = buildT(i+1, rd);for(auto lx : ansLeft)for(auto rx : ansRight){TreeNode *T = new TreeNode(i);T->left = lx;T->right = rx;ans.push_back(T);}}return ans;}vector<TreeNode *> generateTrees(int n) {// write your code herevector<TreeNode *> ans = buildT(1, n);return ans;} };分析:在確定當(dāng)前結(jié)點(diǎn)X后,那么X的左孩子結(jié)點(diǎn)(或右孩子結(jié)點(diǎn))可能會有多個,那么就把這些可能的結(jié)點(diǎn)都存到vector中,然后從左孩子集合中任選出lx結(jié)點(diǎn),以及從右孩子集合中選出rx結(jié)點(diǎn),那么lx和rx就確定了一種形態(tài)的二叉樹。
轉(zhuǎn)載于:https://www.cnblogs.com/hujunzheng/p/5040334.html
總結(jié)
以上是生活随笔為你收集整理的n个结点,不同形态的二叉树(数目+生成)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lintcode 单词接龙II
- 下一篇: codeforces Restore C