当前位置: 首页 > article >正文

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/

不同形态二叉树的数目:

样例

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

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

分析

   可以分析,当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个数依次入栈,出栈顺序有多少种?",  同样用的也是卡特兰数。

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

 

构建不同形态二叉树:

样例

  给出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的左右子树。每一步不断地重复上述过程,最终会得到所有形态的二叉树。

算法实现

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

错误代码:

/*** 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;//节点的总数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<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;}
};

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

  当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结点的情况了。

正确代码:

/*** 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;}
};

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

http://www.lryc.cn/news/2420932.html

相关文章:

  • Netty22——用Netty实现RPC
  • word中拼写希腊字母
  • ASP:FileUpload控件(文件上传控件)
  • 查询EI检索号的方法
  • ant学习-使用ant生成jar包
  • 雅虎免费邮箱开通POP3和自动转发的方法
  • gps信号用什么软件测试,gps信号检测软件
  • Access数据库及注入方法
  • JS定时器的用法及示例
  • AI妻子生成器:科技陪伴,情感无限
  • 解决size mismatch for embedding.embed_dict.userid.weight
  • 单片机——LCD1602
  • 移动测试之-流量测试方案
  • Visual Studio 2008 试用版评估期已结束的解决方法
  • 一步步优化JVM七:其他
  • 无法启动计算机上的服务msdtc,MSDTC服务无法启动解决方法
  • 分享116个ASP搜索链接源码,总有一款适合您
  • Hello C++
  • 纳什均衡定义、举例、分类
  • 开启游戏别样体验:《下一站江湖2》风灵月影六十项修改器使用手册
  • ubuntu9.10 软件推荐
  • Oracle DB Time 解读
  • 收集一些有质感、有内涵的网站 (转载)
  • 实时监控系统介绍
  • MapInfo是一种流行的地理信息系统(GIS)软件,它提供了丰富的功能和工具,用于处理、分析和可视化地理空间数据
  • CAN总线学习笔记 | CAN基础知识介绍
  • 2024年最全在线查询默认密码网站--分享_hawel-lutuo默认密码(1),分析网络安全未来几年的发展前景
  • java计算机毕业设计电商网站在线客服(附源码+springboot+开题+论文+部署)
  • 递归和迭代_深究递归和迭代的区别、优缺点及实例对比
  • 网络层 IPV4报文格式