您的位置:首页 > 其它

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

2015-03-29 11:49 405 查看
描述:

我们可以把由“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");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: