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

来源:互联网 发布:excel 重复数据统计 编辑:程序博客网 时间:2024/05/16 10:25


描述:

我们可以把由“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
原创粉丝点击