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

正确代码:

/**
 * 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 here
        vector<TreeNode *> ans = buildT(1, n);
        return ans;
    }
};

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

时间: 2024-10-25 21:38:44

n个结点,不同形态的二叉树(数目+生成)的相关文章

二叉树的生成

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections.Generic; namespace ConsoleApplication4 {     class Program {         static void Main(string[] args) {       

树-二叉树的基本概念

二叉树的特点 每个结点至多有二棵子树(即不存在度大于2的结点) 二叉树的子树有左.右之分,且其次序不能任意颠倒 卡特兰数 具有n个结点的不同形态的二叉树数目,即所谓的n阶卡特兰数.(也是含有n个结点的栈的出队顺序的总情况) 二叉树的性质(约定空二叉树的高度为-1) 高度为h>=0的二叉树至少有h+1个结点. 高度为h>=0的二叉树至多有2h+1-1个结点 含有n>=1个结点的二叉树的高度至多为n-1 含有n>=1个结点的二叉树的高度至少为[log2n](向下取整) 对于任何一棵二叉

二叉树中第一条最长的路径并输出此路径上各结点的值

本文采用递归方法和配合STL模板的容器实现最长结点的遍历. 实现代码如下: 1 #include<iostream> 2 #include<vector> 3 4 using namespace std; 5 6 typedef struct node 7 { 8 char data;//结点数据 9 struct node *lchild,*rchild;//二叉树结点类型 10 }BSTree;//二叉树结点类型 11 12 vector<char> path; 1

求解二叉树中两个结点的最低公共父结点

一,问题描述 构建一棵二叉树(不一定是二叉查找树),求出该二叉树中某两个结点的最低公共父结点.借用一张图如下: 结点8 和 结点5 的最低公共父结点为 结点2 二,二叉树的构建 与 求二叉树中第K层结点的个数 文章中的第二点:二叉树构建相同 三,求解最低公共父结点的算法实现 有两种思路,一种是通过中序遍历和后序遍历.由于中序遍历是先左子树中的结点,再访问根,再访问右子树中结点,因此这两个结点的公共父结点一定处于这两个结点之间. 如:中序遍历:8, 4, 9, 2, 5, 1, 6, 3, 7  

计算二叉树中叶子结点个数的方法

基础知识: 1.二叉树第i层最多有2^(i-1)个结点. 2.深度为k的二叉树至多有2^k-1个结点. 一个完全二叉树有七百个结点,问该二叉树有多少个叶子结点 根据“二叉树的第i层至多有2^(i − 1)个结点:深度为k的二叉树至多有2^k − 1个结点(根结点的深度为1)”这个性质:因为2^9-1 < 700 < 2^10-1 ,所以这个完全二叉树的深度是10,前9层是一个满二叉树,这样的话,前九层的结点就有2^9-1=511个:而第九层的结点数是2^(9-1)=256所以第十层的叶子结点数

面试题8:二叉树的下一个结点

// 面试题8:二叉树的下一个结点 // 题目:给定一棵二叉树和其中的一个结点,如何找出中序遍历顺序的下一个结点? // 树中的结点除了有两个分别指向左右子结点的指针以外,还有一个指向父结点的指针. // 二叉树的结构体定义如下. struct BinaryTreeNode { int m_nValue; BinaryTreeNode* m_pLeft; BinaryTreeNode* m_pRight; BinaryTreeNode* m_pParent; }; 解题思路: 以上图为例,节点有

数据结构开发(23):二叉树中结点的查找、插入、删除与清除操作

0.目录 1.二叉树中结点的查找操作 2.二叉树中结点的插入操作 3.二叉树中结点的删除操作 4.二叉树中结点的清除操作 5.小结 1.二叉树中结点的查找操作 查找的方式: 基于数据元素值的查找 BTreeNode<T>* find(const T& value) const 基于结点的查找 BTreeNode<T>* find(TreeNode<T>* node) const 树中数据元素和结点的查找: 基于数据元素值的查找: 定义功能:find(node,

Swust OJ975: 统计利用先序遍历创建的二叉树的度为2的结点个数

题目简述 利用先序递归遍历算法创建二叉树并计算该二叉树度为2结点的个数 输入 接受键盘输入的由大写英文字符和"#"字符构成的一个字符串(用于创建对应的二叉树). 输出 输出该用例对应的二叉树度为2的结点个数. 样例输入复制 ABCD###EF##G##H## 样例输出复制 3知识点:二叉树每个结点至多只有两棵子树,即二叉树中不存在大于2的结点故所求二叉树度为2的结点,即既有左孩子也要有右孩子 void DegreeTwo(Tree *&tree) { if(tree!=NULL

树和二叉树

以下的内容做为学习笔记,复制别人的,感觉总结的比较好: 第5章 树和二叉树 本章中主要介绍下列内容:  1.树的定义和存储结构  2.二叉树的定义.性质.存储结构  3.二叉树的遍历.线索算法  4.树和二叉树的转换  5.哈夫曼树及其应用课时分配:     1.2两个学时,3四个学时,4两个学时, 5两个学时,上机两个学时重点.难点:     二叉树的遍历.线索算法.哈夫曼树及其应用 第一节 树 1.树的定义和基本运算1.1 定义    树是一种常用的非线性结构.我们可以这样定义:树是n(n≥