明明一道普及-,我却前前后后挠了快一个小时头皮。。。。
太菜了QAQ
不过最终是想出来了,经过这道题,更加理解了二叉树的结构和遍历序列,比较有意义,来分享一下。
题意非常简单,给出二叉树的中序遍历和后序遍历,求这颗二叉树的先序遍历。
一开始拿到这个题有点不知所措,不知如何遍历,别说是先序遍历了,就是找出每个节点的位置,也是毫无头绪 (虽然找出了位置就可以搞出先序遍历) 。
经过一番思考发现,这个题,必须要分析二叉树节点在中序和后序遍历两种方式的分布规律。具体点说,是二叉树上某一节点位置及其子树的分布。
这里有一个思想很好的帮助了我,就是将节点的子树看成是一个整体,即一个节点的某个子树在遍历序列中必定是连续分布的。
顺着这个思路,就可以对已知数据进行分析了。
中序遍历序列
一个节点的左右子树一定连续分布在该节点的两侧(左子树在左边,右子树在右边)。
用个图来表示:
左右子树的根,即当前节点的左右子节点,存在于左右子树区间内的某个位置
再来一个图,图中kl和kr分别是k的左子节点和右子节点,具体是那个元素仅靠中序序列无法确定:
后序遍历序列
一个节点的左右子树一定连续分布在该节点的左侧,且左子树在左边,右子树在左子树与节点之间。
看图:
而在后序遍历中,一棵树的根节点的在整棵树中的位置是确定的,在整棵树序列的末尾。
看图:
这样一来,不难发现两种遍历序列各自的长短处:
中序遍历:知道左右子树的分布,可根据左右边界和根节点位置计算出左右子树大小。但是不知道左右子节点的位置。
(左右边界表示以该节点为根的树在序列上的分布最左和最右位置)
后序遍历,知道左子节点的位置的位置,不知道左右子树的大小,需要左子树的大小计算出右子节点的位置上。
然后惊喜的发现,两者的长短处可以互补,进而刻画出整棵树。
大概步骤就是:
- 从后序序列拿出一个节点
- 找到中序序列中的对应位置
- 根据左右边界计算出左右子树的大小
- 在后序序列中确定左右子节点
- 递归这个过程。
完成以上想法,还需要一个映射:节点到中序遍历下标的映射,便于从后序序列中得到节点,确定到中序序列上。
最后,就是递归的顺序和问题处理的边界:
从根节点开始的递归,采用先序遍历方法,边遍历边打印出结果。
后序序列去最后一个元素开始。(根节点位于整棵树的右侧)
中序序列的初始边界为首元素和尾元素。(整棵树都是以根节点为根的)
注:注意判断左右子树不存在的情况!!!
代码:
import java.util.Scanner;
public class Main {
static Scanner scan = new Scanner(System.in);
static char[] mid = scan.next().toCharArray();
static char[] back = scan.next().toCharArray();
static int[] pos = new int[100];
//以后序序列中第k为为根的子树,在中序序列中的边界
static void dfs(int l,int r,int k)
{
int size = 1;
System.out.print(back[k]);
if(l == r)
{
return;
}
int po = pos[back[k]];//该节点在中序序列的位置
if(po > l)//有左子树先遍历左子树
{
dfs(l,po - 1,k - 1 - r + po);
}
if(po < r)//有右子树再遍历右子树
{
dfs(po + 1,r,k - 1);
}
}
public static void main(String[] args)
{
for(int i = 0;i < mid.length;i++)
{
pos[mid[i]] = i;
}
dfs(0,mid.length - 1,mid.length - 1);
}
}