二叉树的题,呵呵,说句实话,不喜欢做这种题,为什么,题目看着都感觉好抽象啊,不过好奇总是害死猫。
先看看题目吧:
我们可以用如下如下方法给二叉树编号:
(1) 空树编号为0
(2) 只有一个结点的树编号为1
(3) 对任意非负整数m,包含有m个结点的二叉树编号笔包含有(m + 1)个结点的二叉树编号小
(4) 对一个包含有m个结点的二叉树,假设它左子树编号是L,右子树编号是R,它的编号是n,当且仅当,所有编号大于n并且包含m个结点的二叉树,满足以下如下条件:
(a) 其左子树编号大于L
或者
(b) 其左子树编号等于L,并且右子树编号> R
下图是编号为0-9的二叉树,以及编号为20的二叉树。
现给定编号n(1<=n <=500000000),求编号为n的二叉树。
二叉树的左孩子和右孩子递归表示。
即 单个结点用X表示,
如果二叉树只有左孩子L,则要表示成(L)X
如果二叉树只有右孩子R,则要表示成X(R)
否则,左右子树都要表示,即表示成(L)X(R)。
例如编号为20的树表示成:
((X)X(X))X
题目的意思就是给定序号的二叉树,用个字符串表示出来。
现在来分析下这个二叉树的排序规律。二叉树跟节点数相关,所以,我们就吧这些二叉树首先按照这点个数分类。
节点个数为0,只有一种情况,记做tree[0]=1;
节点个数为1,也只有一种,记做tree[1]=1;
分析下来很容易得到:
节点个数为n的二叉树的个数:tree[n]=tree[0]*tree[n-1]+tree[1]*tree[n-2]+.....+tree[n-1]*tree[0]
那么对于给定的序号n,就表示有n+1个树。(因为是从0开始编号的嘛),由上面的公式,我们很容易用递归调用得到编号为n的树,左树有多少个节点leftnode,右树有多少个节点rightnode
下一步是是要知道,左树在这leftnode个节点中,是第几个,右树在这rightnode个节点中,是第几个。这个可以从序号规律得到。
函数:m表示当前树有多少个节点,x表示还剩下多少个(即已经减去0~m-1节点出现的可能情况。nodes就是前面的树数组。
static string cal(int m, int x,int[] nodes)
{
string result = "";
int count = 0;
int nodessum = 0;
bool flag = false;
for (int i = 0; i < m; i++)
{
nodessum = nodes[i] * nodes[m - 1 - i];
count += nodessum;
if (x > nodessum)
{
x -= nodessum;
}
else
{
double d = (double)x / (double)nodes[m - 1 - i];
//这里就是求左树有i个节点时,第多少个
int leftindex = (int)Math.Ceiling(d);
//右树第多少个
int rightindex = x % nodes[m - 1 - i];
if (rightindex == 0)
{
rightindex = nodes[m - 1 - i];
}
//print函数就是把有m个节点,第x个的树用字符串表示出来
string leftstr = print(i, leftindex, nodes);
string rightstr = print(m - 1 - i, rightindex, nodes);
if (leftstr == "")
{
result = "X";
}
else
{
result = "(" + leftstr + ")X";
}
if (rightstr == "")
{
}
else
{
result = result + "(" + rightstr + ")";
}
flag = true;
break;
}
}
if (!flag)
{
nodes[m] = count;
result = cal(m + 1, x, nodes);
}
return result;
}
现在来看print函数:
static string print(int m, int index, int[] nodes)
{
string result = "";
if (m == 1)
{
return "X";
}
if (m == 0)
{
return "";
}
int nodessum = 0;
for (int i = 0; i < m; i++)
{
nodessum = nodes[i] * nodes[m - 1 - i];
if (index > nodessum)
{
index -= nodessum;
}
else
{
double d = (double)index / (double)nodes[m - 1 - i];
int leftindex = (int)Math.Ceiling(d);
//int leftindex = (int)Math.Ceiling((double)(index / nodes[m - 1 - i]));
int rightindex = index % nodes[m - 1 - i];
if (rightindex == 0)
{
rightindex = nodes[m - 1 - i];
}
string leftstr = print(i, leftindex, nodes);
string rightstr = print(m - 1 -i, rightindex, nodes);
if (leftstr == "")
{
result = "X";
}
else
{
result = "(" + leftstr + ")X";
}
if (rightstr == "")
{
}
else
{
result = result + "(" + rightstr + ")";
}
break;
}
}
return result ;
}
可以看到print函数和cal函数有很大一部分是完全相同的,一个是从前向后找到第多少个节点,一个是知道了多少个节点,和序号,向前递推,输出字符串。感觉应该可以综合下。
最后就是在main函数里面调用,初始化nodes数组
int[] nodes = new int[1000];
nodes[0] = 1;
nodes[1] = 1;
string result = "";
if (x == 1)
{
result = "X";
}
else
{
result = cal(2, x-1 , nodes);
}
return result;
其实nodes数组可以初始化小点,从前面分析可以看出,节点数增加时,其个数增加非常快,基本上序号达到500000000时,节点估计也在100以内,好像才18吧。
ok,提交,通过。