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

Posted Edolon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树の讲解-----二叉树入门(例题)相关的知识,希望对你有一定的参考价值。

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

题目

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

以上是关于树の讲解-----二叉树入门(例题)的主要内容,如果未能解决你的问题,请参考以下文章

线段树の一 区间和

数据结构-遍历序列求二叉树

阿里高频题动画讲解二叉树深度优先遍历

LeetCode刷题日记精选例题(代码+链接)

初识“回溯算法”讲解及LeetCode对应例题解析

初识“回溯算法”讲解及LeetCode对应例题解析