博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
n个结点,不同形态的二叉树(数目+生成)
阅读量:6278 次
发布时间:2019-06-22

本文共 5140 字,大约阅读时间需要 17 分钟。

v题目链接:

  不同的二叉查找树:

  不同的二叉查找树 II:

v不同形态二叉树的数目:

v样例

  给出n = 3,有5种不同形态的二叉查找树:

  1           3    3       2      1   \         /    /       / \      \    3      2     1       1   3      2   /      /       \                  \  2     1          2                  3

v分析

   可以分析,当n=1时,只有1个根节点,则只能组成1种形态的二叉树,令n个节点可组成的二叉树数量表示为h(n),则h(1)=1; h(0)=0;

       当n=2时,1个根节点固定,还有2-1个节点。这一个节点可以分成(1,0),(0,1)两组。即左边放1个,右边放0个;或者左边放0个,右边放1个。即:h(2)=h(0)*h(1)+h(1)*h(0)=2,则能组成2种形态的二叉树。

      当n=3时,1个根节点固定,还有2个节点。这2个节点可以分成(2,0),(1,1),(0,2)3组。即h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=5,则能组成5种形态的二叉树。

以此类推,当n>=2时,可组成的二叉树数量为h(n)=h(0)*h(n-1)+h(1)*h(n-2)+...+h(n-1)*h(0)种,即符合Catalan数的定义,可直接利用通项公式得出结果。

令h(1)=1,h(0)=1,catalan数(卡特兰数)满足递归式: 

  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);

  该递推关系的解为:

  h(n)=C(2n,n)/(n+1) (n=1,2,3,...)

  由此想到了上次说的"N个数依次入栈,出栈顺序有多少种?",  同样用的也是卡特兰数。

   

v代码

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 here        return C(2*n, n)/(n+1);    }};

 

v构建不同形态二叉树:

v样例

  给出n = 3,生成所有5种不同形态的二叉查找树:

  1         3     3       2    1   \       /     /       / \    \    3     2     1       1   3    2   /     /       \                \  2     1         2                3

  其实通过样例,我们可以发现n个结点构造不同形态二叉树的过程,1,2,3.....n个结点,枚举每一个结点为根结点(假设为root, 1<=root<=n), 那么(1,2..root-1)和(root+1, root+2...n)分别是root的左右子树。每一步不断地重复上述过程,最终会得到所有形态的二叉树。

v算法实现

  先弱弱的说一下自己错误的实现,因为递归实现的时候会得到不同的二叉树,那么如何判断n个结点正好生成了二叉树呢?于是用了一个变量useNode(=0),表示当前已经用了多少个结点建树。当useNode等于n的时候说明产生了一棵符合要求的树,接着拷贝一下刚才生成的树,然后放入vector中,继续建造下一棵符合条件的二叉树。

v错误代码:

/** * 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
ans; int cntNode=0;//节点的总数 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){
//这个树已经建立完毕,拷贝一下吧 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
generateTrees(int n) { // write your code here cntNode = n; TreeNode *T = NULL; buildT(T, 1, n, 0); if(n == 0) ans.push_back(T); return ans; }};

  后来运行之后,看到错误的答案与正确答案的对比,如下:

当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}]

  也就是少了{3,1,4,#,2},以3为根结点的二叉树为什么会少了呢?仔细想想,3结点的左孩子可以是1,也可以是2,那么左孩子为1的情况就被忽略了,此时useNode并不等于n,然后就换成左孩子为2结点的情况了。

v正确代码:

/** * 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
buildT(int ld, int rd){ vector
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
ansLeft = buildT(ld, i-1); vector
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
generateTrees(int n) { // write your code here vector
ans = buildT(1, n); return ans; }};

  分析:在确定当前结点X后,那么X的左孩子结点(或右孩子结点)可能会有多个,那么就把这些可能的结点都存到vector中,然后从左孩子集合中任选出lx结点,以及从右孩子集合中选出rx结点,那么lx和rx就确定了一种形态的二叉树。

本文转自 小眼儿 博客园博客,原文链接:http://www.cnblogs.com/hujunzheng/p/5040334.html,如需转载请自行联系原作者
你可能感兴趣的文章
mysqldump和xtrabackup备份原理实现说明
查看>>
[Angular2 Form] Create and Submit an Angular 2 Form using ngForm
查看>>
Atitit.数据检索与网络爬虫与数据采集的原理概论
查看>>
POJ3494Largest Submatrix of All 1’s[单调栈]
查看>>
ofstream的使用方法
查看>>
FFPEG 转码记录------解决了有流,但是没有码率和FPS?
查看>>
Adding ASP.NET MVC5 Identity Authentication to an existing project
查看>>
电台大神打油诗
查看>>
Win8Metro(C#)数字图像处理--2.27图像加法运算
查看>>
字符、字符串和文本的处理之Char类型
查看>>
【读书笔记::深入理解linux内核】内存寻址【转】
查看>>
java 内存泄漏和内存溢出
查看>>
visual studio code 在 mac 下按 F12无效
查看>>
C#与C++的发展历程第四 - C#6的新时代
查看>>
清空nohup日志
查看>>
日语输入中的促音怎么输入
查看>>
:: My Life Organized :: Downloads
查看>>
小工具:ssh-copy-id_老王的技术手册 ( 我的新博客:http://huoding.com )_百度空间
查看>>
[NET]Net中的反射使用入门(根据类名和函数名,生成和调用对象的成员函数) (转)...
查看>>
优秀的Web开发人员是这样炼成的 (share)
查看>>