题目
链接: https://ac.nowcoder.com/acm/contest/19859/O
题意:
给定一棵有n个结点的二叉树的先序遍历与后序遍历序列,求其中序遍历序列。若某节点只有一个子结点,则此处将其看作左儿子结点
输入
5,[3,2,1,4,5],[1,5,4,2,3]
输出
[1,2,5,4,3]
注意:输出的是一个int类型的数组
#解析 :
先说明一下题目给的条件:cond“若某节点只有一个子结点,则此处将其看作左儿子结点”。例子给出的二叉树图,3节点只有一个左子树,但实际上可以把左子树放到3节点的右边(变成右子树),先序后序遍历仍然和题目给出的一样。
这里说下为什么会这样:根据先序和后序遍历的特点,如果一个节点只有左子树(或只有右子树),先序都为 rt ls (rt rs),后序都为 ls rt (rs rt)。只有右子树先序后序遍历也一样。(rt父节点,左右子树ls rt)。例如题目中的节点5可以接在4节点的左边也可以接在右边。你可以发现先序都是45,后序都是54。
二叉树的形态分析:如果没有条件cond,二叉树的形态会有2^k种(k为树中所有 只有一个子结点的 结点的数目),例子中,k=2,共4种形态,3节点的左子树可接到右边,4节点的左子树也可以接到右边。
综上,我们可以得出一个结论,有右子树一定会有左子树。所以编程的时候我们以左子树为主(一直满足pre[l1++]==suf[r2--]条件,说明只有左子树,一直递归左子树,右子树null),当pre[l1]!=suf[r2]时,说明有右子树了。假如cnt为多余的部分,即cnt(suf[k],suf[r2]],suf[k]==pre[l1],向左递归,然后将多余的部分递归右子树即可。
并且我们可以发现suf[r2]==pre[r1-cnt],这就为我们每次递归一次,只要不l1>r1||l2>r2,我们都建树(递归的每个状态都满足pre[l1]==suf[r2],每次进去都建一个节点),判断下一次,如果相等递归左子树,不相等,找到cnt,向两边递归(不相等说明有右子树)
AC代码 - - -
#solution1
import java.util.*;
class Node{
int v;
Node ls,rs;
public Node() {
v = -1;
ls = null;
rs = null;
}
public Node(int v,Node ls,Node rs) {
this.v = v;
this.ls = ls;
this.rs = rs;
}
}
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param n int 二叉树节点数量
* @param pre int一维数组 前序序列
* @param suf int一维数组 后序序列
* @return int一维数组
*/
static int a[],cnt = 0;
static Node build(int l1,int r1,int l2,int r2,int[] pre,int[] suf) {
if(l1>r1||l2>r2) return null;
Node nd = new Node();
nd.v = pre[l1];
int cnt = 0, p = r2;
while(--p>=l2&&l1+1<=r1) {
if(pre[l1+1]==suf[p]) {
break;
}else {
cnt++;
}
}
nd.ls = build(l1+1,r1-cnt,l2,r2-cnt-1,pre,suf);
nd.rs = cnt==0?null:build(r1-cnt+1,r1,r2-cnt,r2-1,pre,suf);
return nd;
}
static void inorder(Node nd) {
if(nd==null) return ;
inorder(nd.ls);
a[cnt++] = nd.v;
inorder(nd.rs);
}
public int[] solve (int n, int[] pre, int[] suf) {
Node nd = build(0,n-1,0,n-1,pre,suf);
a = new int[n];
inorder(nd);
return a;
}
}