树の讲解-----二叉树入门(例题)

上篇博客写的太烂了,我自己都不想看

题目

P1827 [USACO3.4]美国血统 American Heritage

简单来说就是已知中序遍历和前序遍历,求后序遍历

输入第一行为中序遍历,第二行为前序遍历

输入输出样例 


输入   	 输出 

ABEDFCHG  AEFDBHGC
CBADEFGH 

解题思路

我们先来一步步分析,已知中序遍历和前序遍历,根据这三种顺序的定义我们可以知道,我们只需要先遍历一遍这棵树的左子树和右子树最后输出所在子树的根节点就可以得出后序遍历的答案

  • 这题不建议看着深基来做......因为深基上面是先输入前序再输出后序,如果那样程序核心得颠倒一下,这个我们之后再说

先打出程序的主体框架

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
string s1,s2;//s1前序,s2中序
inline void work(int l1,int r1,int l2,int r2)    

//l1:前序中目前所在子树根节点位置 r1:前序中目前所在子树范围终点的位置 
l2:中序中目前所在子树在前序中开始的地方 r2:中序中目前所在子树在s2中结束的地方
{
    /*若干Code*/
}
int main()
{
    cin>>s1>>s2;
    swap(s1,s2);//这题我是用深基上的方法做的,所以交换一下比较方便qwq
    work(0,s1.size()-1,0,s2.size()-1);
    return 0;
}
            

然后我们根据上述的思路打出核心递归代码的大体

for(int i=l2;i<=r2;i++)
    {
        if(s1[l1]==s2[i])//i:目前所在子树根节点的位置(中序遍历中)
        {
            work(l1+1,l1+1+(i-1-l2),l2,i-1);//左子树,实际上等同于work(l1+1,l1+i-l2,l2,i-1)
            work(l1+i-l2+1,r1,i+1,r2);//右子树,这里的第一个值为什么是l1+i-l2+1稍微一想就会非常简单
            printf("%c",s1[l1]);
            return;
        }
    }

总程序

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
string s1,s2;
inline void work(int l1,int r1,int l2,int r2)               
{
    for(int i=l2;i<=r2;i++)
    {
        if(s1[l1]==s2[i])
        {
            work(l1+1,l1+1+(i-1-l2),l2,i-1);
            work(l1+i-l2+1,r1,i+1,r2);
            printf("%c",s1[l1]);
            return;
        }
    }
}
int main()
{
    cin>>s1>>s2;
    swap(s1,s2);
    work(0,s1.size()-1,0,s2.size()-1);
    return 0;
}

好看点的第二遍打的Code

#include<cstdio>
#include<iostream>
using namespace std;
string a,b;//a中序
inline void work(int l1,int r1,int l2,int r2)
{
    for(int i=l1;i<=r1;i++)
    {
        if(a[i]==b[l2])
        {
            work(l1,i-1,l2+1,l2+i-l1);//左子树
            work(i+1,r1,l2+i-l1+1,r2);//右子树
            cout<<b[l2];
            return;
        }
    }
}
int main()
{
    cin>>a>>b;
    work(0,a.size()-1,0,b.size()-1);//l1,r1中序,l2,r2前序
    return 0;
}

P1030 [NOIP2001 普及组] 求先序排列

还是先来上代码一步步讲解

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
string a,b;//a中序,b后序
inline void work(int l1,int r1,int l2,int r2)//l1r1中序,l2r2后序
{
    for(int i=l1;i<=r1;i++)
    {
        if(a[i]==b[r2])
        {
            printf("%c",b[r2]);
            work(l1,i-1,l2,l2+i-1-l1);//左子树
            work(i+1,r1,l2+i-1-l1+1,r2-1);//右子树
            return;
        }
    }

}
int main()
{
    cin>>a>>b;
    work(0,a.size()-1,0,b.size()-1);
    return 0;
}

代码主体啥意思咱也都知道,我们直接讲递归代码

for(int i=l1;i<=r1;i++)
    {
        if(a[i]==b[r2])
        {
            printf("%c",b[r2]);
            work(l1,i-1,l2,l2+i-1-l1);//左子树
            work(i+1,r1,l2+i-1-l1+1,r2-1);//右子树
            return;
        }
    }

首先因为是求先序排列,所以我们把输出(即根节点)放在最前面,再进行左右子树的遍历,因为这次知道的是后序遍历,后序遍历的根节点放在后面,所以我们输出的是 b[r2] (不明白的写个前中后遍历就知道了)

然后我们看左子树的遍历

work(l1,i-1,l2,l2+i-1-l1);

前两个变量好解释,是中序遍历里左子树的范围,我们举个例子

中序遍历:4352617
后序遍历: 4536271

后序遍历根节点再后面,所以我们做个Sign,标记一下根节点的位置,也就是枚举的 \(i\) 的值

左子树的范围
         l1               i-1
中序遍历:4    3   5   2   6   |1|  7

         l2            l2+(i-1-l1)
         |                | 
后序遍历: 4    5   3   6   2    7  |1|

再看右子树的范围

 work(i+1,r1,l2+i-1-l1+1,r2-1);

右子树的范围
                                  i+1/r1(实际上是重合了)
                                   |
中序遍历:4    3   5   2   6   |1|  7

                     		l2+i-1-l1+1/r2-1(同上)
                                | 	
后序遍历: 4    5   3   6   2    7  |1|

然后就没有然后了

Ending

posted @ 2021-06-11 19:25  Edolon  阅读(61)  评论(0编辑  收藏  举报
Live2D