#背景:
树和二叉树基本上都有先序、中序、后序、按层遍历等遍历顺序,给定中序和其它一种遍历的序列就可以确定一棵二叉树的结构。
现在给出它的后序遍历和中序遍历,请你构建这棵二叉树。
#正文:
先中和后中的思路相同,这里以先中举例:
思路:通过递归,不断压缩中序和先序序列的长度,从而确定左右子树。左右子树的根节点就是左右孩子。
- 根据先序遍历的性质,先序序列中的第一个元素就是根节点
root
。确定该点在中序序列中的位置k
。 - 中序序列中:该位置的左边都是左子树,右边都是是右子树。
- 左子树的根节点就是根节点
root
的左孩子,右子树的根节点就是根节点root
的右孩子。 - 递归到左子树和右子树。
- 一直递归。。。如此,便 求出了每个节点的左右孩子。
这是大体思路,如何具体实现呢?
给出一个例子:
根据先序序列,我们可以看出这个树的根节点是节点A。之后,我们定位到中序序列中的位置
k
。
那么,根据中序遍历的特点,这个位置左边就是根节点的左子树,这个位置右边就是根节点的右子树。
接着,我们递归到这两个子树就行了。
那就需要分别将包含这两个子树的序列挑出来,之后重复上述过程就行了。然而,
如何挑出包含左子树(右子树)的两个序列呢?
1、对于中序序列,很容易将左子树(右子树)的序列挑出来:就是根节点左方(右方)的所有元素。
2、而对于挑出左子树(右子树)的先序序列,就要转换下了:
-
根据先序遍历的特点,在先序序列中,左右子树也是分开的(先遍历左子树,再是右子树),只是,分界线不清楚。
那么,我们可以用中序数列求出的左子树或右子树的个数,求出先序序列中左右子树的分界点! -
从下面的图中可以看出,根节点为
A
,左子树中的点:C D B
,右子树中的点:F E G
关键点:找出先序序列中左右子树的分界点:
首先,设根节点A
的位置为k
方法1:用两种序列中,左子树的个数相等,求出分界点
- 中序数列中左子树的个数:
k-il
- 先序数列中左子树的个数:
x-pl
(x
为左右子树分界线左边的一个位置,即图中D
的位置) - 两种数列左子树的个数是相等的,那么,
x=k-il+pl
方法2:用两种序列中,右子树的个数相等,求出分界点
-
中序数列中右子树的个数:
ir-k
-
先序数列中右子树的个数:
pr-x+1
(x
为左右子树分界线右边的一个位置,即图中E
的位置) -
两种数列右子树的个数相等,那么,
x=pr+1-ir+k
综上:
左子树的中序序列:
il
到k-1
左子树的先序序列:
pl+1
到k-il+pl
(或pl+1
到pr-ir+k
)右子树的中序序列:
k+1
到ir
右子树的先序序列:
k-il+pl+1
到pr
(或pr+1-ir+k
到pr
)
哈哈,还迷糊的话就看代码吧:
struct T{
int l, r;
}a[N];
int build(int pl, int pr, int il, int ir)
{
int root = pre[pl]; //root为先序中的第一个元素
int k = mp[root]; //k为该子树根节点root在中序数列中的位置
if(k > il) a[root].l = build(pl+1, k-il+pl, il, k-1);
//k>il,中序数列中,根节点左边有位置,则说明有左子树,就更新
if(k < ir) a[root].r = build(k-il+pl+1, pr, k+1, ir);
return root; //父节点的左孩子是左子树的根节点(或右孩子是右子树的根节点),故返回根节点
}
int main(){
cin >> n;
for(int i=1;i<=n;i++) cin >> pre[i];
for(int i=1;i<=n;i++){
cin >> post[i];
mp[post[i]] = i; //记录每个点在中序数列中的位置
}
build(1, n, 1, n); //最大的树
return 0;
}
类似,根据后序和中序也可以建树:
struct T{
int l, r;
}a[N];
int build(int il, int ir, int pl, int pr)
{
int root = post[pr];//该段后序中的最后一个元素就是该子树的根
int k = mp[root]; //找出此根在中序中的位置k
if(il < k) l[root] = build(il, k-1, pl, k+pl-il-1); //k前面有数,说明有左子树,则其左节点为前面一段中,后序的最后一个(递归到下一段)
if(ir > k) r[root] = build(k+1, ir, k+pl-il, pr-1);
return root; //该树的根
}
int main(){
cin >> n;
for(int i=1;i<=n;i++) cin >> post[i];
for(int j=1;j<=n;j++) cin >> in[j], mp[in[j]] = j;
build(1, n, 1, n);
return 0;
}
最后的代码是不是挺简单的?也许,这就是递归算法的优美之处吧~~