多叉树(森林)转二叉树

本来不怎么想写这个,但发现网上的都是“残疾”博客,讲得不是很详细,所以我还是要写一下。
多叉转二叉有“左儿子右兄弟”的说法,然而对于什么都不知道的小白,这句话没有任何用……

思路

大体就两步,很好理解,如图是用来举栗的多叉树:

兄弟连

将所有的兄弟间连一条线,如图:

右子断

将所有右儿子与父亲的边删掉,如图:

其他

事实上这已经是一棵二叉树了,还有一点小细节。

调整层次

这里写图片描述
很容易发现,“兄弟”(即黄色线相连的结点)都在它们的大哥右子树中,且根结点一定没有右儿子(因为根结点没有兄弟)。

森林的处理

有的坑爹题目,转为二叉树后有多个结点的父亲都为0(即为根结点),如图:
这里写图片描述
(原谅我直接copy的第一棵树,不过没影响)解决这个很容易,从右到左依次将根结点接到它前一个根结点的右儿子上即可:
这里写图片描述

实现

连接兄弟&切断右儿子

这里介绍最好理解的方法:
输入一对关系,如x,y(表示y为x的儿子)
判断:x有没有左儿子
没有:y直接成为x的左儿子;
有:设x的左儿子为x.l,右儿子为x.r,就找到x.l的右儿子的右儿子的右儿子……
为什么不用切断右儿子?因为根本就还没有连接右儿子!
如图:
这里写图片描述

就这样。(这么简单?!)
代码:

if(tree[i].l==0)  
{  
    tree[i].l=j;  
    tree[j].f=i;//改变父子关系  
}  
else  
{  
    int t=tree[i].l;  
    while(tree[t].r)  
        t=tree[t].r;//找到x左子树中最右边的结点  
    tree[t].r=j;  
    tree[j].f=t;  
}  

森林

按照刚刚说的硬行实现即可:

for(int i=1;i<=N;i++)  
    if(!tree[i].f)  
        root[++rs]=i;//找到所有根结点并保存
for(int i=rs;i>1;i--)  
{  
    tree[root[i-1]].r=root[i];
    tree[root[i]].f=root[i-1];//处理根结点
}

完整代码

恶心的是这道题兄弟之间要要求顺序(编号小的在左边),所以不能边输入边处理

//输入N,M表示节点数和边数,再输入M组边关系,从1到N输出每个结点的父亲,根节点父亲为0 
#include<cstdio> 
#include<algorithm>  
using namespace std;  
void read(int &x)  
{  
    int f=1;x=0;char s=getchar();  
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}  
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}  
    x*=f;  
}  
#define MAXN 100  
struct node  
{  
    int l,r,f;  
}tree[MAXN+5];  
int N,M;  
int root[MAXN+5],rs;  
bool d[MAXN+5][MAXN+5];  
void rtb()//处理森林
{  
    for(int i=1;i<=N;i++)  
        if(!tree[i].f)  
            root[++rs]=i;  
    for(int i=rs;i>1;i--)  
    {  
        tree[root[i-1]].r=root[i];  
        tree[root[i]].f=root[i-1];  
    }  
}  
int main()  
{  
    read(N),read(M);  
    for(int i=1;i<=M;i++)  
    {  
        int x,y;  
        read(x),read(y);  
        d[x][y]=1;//先保存起来
    }  
    for(int i=1;i<=N;i++)//再处理
        for(int j=1;j<=N;j++)  
            if(d[i][j])  
            {  
                if(tree[i].l==0)  
                {  
                    tree[i].l=j;  
                    tree[j].f=i;  
                }  
                else  
                {
                    int t=tree[i].l;
                    while(tree[t].r) t=tree[t].r;
                    tree[t].r=j;
                    tree[j].f=t;
                }
            }
    rtb();
    for(int i=1;i<=N;i++)  
    {  
        if(i<N) printf("%d\n",tree[i].f);  
        else printf("%d",tree[i].f);  
    }  
}  
  • 18
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值