Vijos1114解题报告(不建树解决二叉树问题)


描述:

我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。

FBI树是一种二叉树1,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2^N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:
1) T的根结点为R,其类型与串S的类型相同;
2) 若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2。

现在给定一个长度为2^N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历2序列。

 

解题思路:使用函数求出每个节点,然后通过该节点构建树,最后遍历树即可。

 

难点:建树函数void buildtree(int start, int end, treeNode* &root) 当start与end相等时,如果递归参数不当,如构建左子树使用函数buildtree(start, (start+end)/2, root->lc),会陷入无限递归的深渊。

处理方法:

1:求串的左半边时用(start+end+1)/2-1;求右半边用(start+end)/2+1, 构建函数最开始用判断条件start>end时return;

2: 构建函数在递归调用前判断start是否等于end如果等于不递归调用函数;


代码:

#include <cstdio>
#include <cstring>
#include <cmath>

const int MAXN = 2000;
char str[MAXN];
int nodenum;
class treeNode
{
    public:
    treeNode *lc;
    treeNode *rc;
    char c;
};
char findRoot(int start, int en)
{
    if(start > en)
        return 'N';
    int i;
    char first = str[start];

    for(i=start; i <= en; i++)
    {
        if(str[i] != first)
            return 'F';
    }
    if(first == '0')
        return 'B';
    return 'I';

}
void buildtree(int start, int end, treeNode* &root)
{
    if(start > end)
    {
        root = NULL;
        return;
    }
    char rootc;
    rootc = findRoot(start, end);
    root = new treeNode;
    root->c = rootc;
    buildtree(start,(start + end + 1) / 2-1, root->lc);
    buildtree((start + end) / 2+1, end, root->rc);
}
void lvisit(treeNode *root)
{
    if(root == NULL) return;
    lvisit(root->lc);
    lvisit(root->rc);
    printf("%c", root->c);
}
int main()
{
    scanf("%d", &nodenum);
    scanf(" %s", str);
    nodenum = pow(2, nodenum);
    treeNode *root = NULL;
    buildtree(0, nodenum-1, root);
    lvisit(root);
    printf("\n");
}

优化:

上面的方法过于麻烦:

其实由于树是先序建立的所以如果建立根结点后,递归建立左子树和右子树前就把节点的值输出,那么树建立结束的同时就输出树的先序遍历序列。如果在建立左右子树后再输出节点的值,那么最后输出了树后序遍历序列。如果在建立左子树后,建立右子树前输出节点的值,那么最后输出了树的后序遍历序列。

只要符合先序建树的条件就可以用这种方法。

这样以来就不用保存树的节点,也不用在遍历树了。减少了时间复杂度和空间复杂度。


代码如下:

使用处理方法1:

#include <cstdio>
#include <cstring>
#include <cmath>
const int MAXN = 1050;
char str[MAXN];
int nodenum;
char findRoot(int start, int en)
{
    while(start<en)
    {
        if(str[start] != str[en])
            return 'F';
        start++;
    }
    if(str[en] == '0')
        return 'B';
    return 'I';
}
void buildtree(int start, int end)
{
    if(start > end)
        return;
    buildtree(start,(start + end + 1) / 2-1);
    buildtree((start + end) / 2+1, end);
    printf("%c", findRoot(start, end));
}
int main()
{
    scanf("%d", &nodenum);
    scanf(" %s", str);
    buildtree(0, pow(2, nodenum)-1);
    printf("\n");
}

进一步优化:

在求每个节点时,其实不用重新遍历串了,只需根据该节点左右孩子就可以确定该节点是F还是B还是I。如果左右节点不同,该节点为F,否则该节点和其左右节点相同。这样时间复杂度会进一步降低,代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
const int MAXN = 1025;
char str[MAXN];
int nodenum;
char buildtree(int start, int end)
{
    char a;
    if(start != end)
    {
        a = buildtree(start,(start + end)/2);
        if(a!= buildtree((start + end)/2+1, end)) a = 'F';
        printf("%c", a);
        return a;
    }
    if(str[start] == '0') a = 'B';
    else a = 'I';
    printf("%c", a);
    return a;
}
int main()
{
    scanf("%d", &nodenum);
    scanf(" %s", str);
    buildtree(0, pow(2, nodenum)-1);
    printf("\n");
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值