ZJU1062 对树的排序

ZJU1062: Trees Made to Order

我们以这样的规则给二叉树编号
结点数为0的树编号为0
只有一个结点的树编号为1
当有两个结点时,第一个结点为根。第二个结点先放在右边,就下图的(2),然后放在左边,就是下图的(3)当有三个结点时也是这样,先右再左。大家自己看图吧

 

输入树的编号N(1 <= n <= 500,000,000)输出树的结构

输入:若干行整数N,以N=0的空数据结束
输出:按示例(L)X(R)的中序遍历格式输出每一个树,每个一行

Sample Input

1
20
31117532
0

Sample Output

X
((X)X(X))X
(X(X(((X(X))X(X))X(X))))X(((X((X)X((X)X)))X)X)

题目分析:

要想构造出一棵编号为N的二*树,我们只需要知道左子树的编号和右子树的编号,就可以进行递归构造了。我们目前所能得到的额外的有用信息,就是具有n个结点的不同形态二*树的总数目Bn = C(2n, n) / (n + 1),但是这个公式的计算较为复杂。我们采用下面的方法来计算:
while(1)//f[t]是结点树目为 t 时二叉树的数目 
     {
         t++;
         for(i=0;i<=t-1;i++)
          f[t]+=f[i]*f[t-1-i];//f[i]为右子树的个数  f[t-1-i]为左子树的个树
         s+=f[i];
        if(s>500000000)
        break; 
     }   
用递推的方法来计算,n个结点的二叉树目=右子树的个数*左子树的个树,编程起来较为容易实现。


我们考虑采用逼近的思想。首先,计算出编号为n的二*树有几个结点:将n依次减去f[1],f[2],...,f[i],直到不能减为止,即此时n<f[i],设则此二*树共有t个结点,除去根结点,则左右子树共有t-1个结点。然后,计算左右子树各有几个结点:将n依次减去f[i]*f[t-1-i],i=t-1,t-2,...0,直到不能减为止,则左子树有t-1-i个结点,右子树有i 个结点。最后,计算左子树是具有t-1-i个结点的二*树中的第x棵,右子树是具有i个结点的二*树中的第y棵:将n整除f[i], x=m/f[i];
                                            if(m%f[i]!=0) x++;。
                                            y=(m-1)%f[i]+1;

(上面的公式只要对照例图看就可以得出,不过我还是想了很长的时间(注意是先排右子树,再排左子树的))


因此,我们求可得左子树的编号和右子树的编号,递归求解即可。f[n]可以在程序开始前用递推的方法高效地预先计算好,以后直接调用即可。

这道题用的逼近的思想,是一种非常重要的思想。对于一些全局统计的问题,如果可以进行局部统计,则可以利用局部统计的结果,逐步逼近全局统计,在逼近的过程中就可以计算出一些中间变量,利用它们便可以解决问题。

例程:

#include<iostream>
#include<string>
using namespace std;
long f[30];
long n;
void init()//递推计算结点为T时二叉树的数目 
 {
     memset(f,0,sizeof(f));
     int i,t;
     long s;
     s=t=0;
     f[0]=1;
     while(1)//结点为T时二叉树的数目 
     {
         t++;
         for(i=0;i<=t-1;i++)
          f[t]+=f[i]*f[t-1-i];
         s+=f[i];
        if(s>500000000)
        break; 
     }   
    
 }
void build(int t,long m)
 {
     int i;
     long k;
     i=t-1;
     //i为右子树的结点个数,t-1-i为左子树的个数
     while(m>f[i]*f[t-1-i])//f[i]为右子树的个数  f[t-1-i]为左子树的个树
       {
           m=m-f[i]*f[t-1-i];
           i--;
       }
     if(t-1-i>0)  //有左子树   
       {
           cout<<"(";
           k=m/f[i];//计算左子树是具有t-1-i个结点的二*树中的第k棵
           if(m%f[i]!=0) k++;
           build(t-1-i,k);
           cout<<")";
                                
       } 
      cout<<"X";
      if(i>0)
       {
           cout<<"(";  //右子树是具有i个结点的二*树中的第k棵
           k=(m-1)%f[i]+1;
           build(i,k);
           cout<<")";
       }      
 }      
 int main()
  {   

    int i;
      init();
      while(cin>>n&&n!=0)
        {
            i=1;
            while(n>f[i])//计算出编号为n的二*树有几个结点
              {
                  n=n-f[i];
                  i++;
              } 
            build(i,n);
            cout<<endl;   
           
        }   
     
      return 0;
  }    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值