树
给一棵点带权(权值各不相同 ,都是小于10000的正整数)的二叉树的中序和后序遍历,找一个叶子结点使得它到根节点的路径上的权和最小。如果有多解,该叶子本身的权应尽量小,输入中每两行表示一棵树,其中第一行为中序遍历,第二行为后序遍历。
样例输入
3 2 1 4 5 7 6
3 1 2 5 6 7 4
7 8 11 3 5 16 12 18
8 3 11 7 16 18 12 5
255
255
样例输出
1
3
255
题记
本题最重要的一点就是要知道,通过一棵树的中序遍历和后序遍历可以唯一确定一棵二叉树,并且后序遍历的最后一个就是这颗二叉树的根节点,在中序遍历当中,根结点左边的就是左子树,右边的就是右子树。因为本题比较特殊,所有结点的权值各不相同,所以可以根据权值来用数组唯一标识一个结点,而不用结构体。
代码如下
#include <iostream>
#include <string>
#include <algorithm>
#include <sstream>
using namespace std;
const int maxv=10000+10;
int in_order[maxv],post_order[maxv],lch[maxv],rch[maxv];//中序遍历,后序遍历,左孩子,有孩子
int n; //记录节点个数
bool read_list(int *a){ //读取序列
string line;
if(!getline(cin,line))
return false;
stringstream ss(line);
n=0;
int x;
while(ss>>x)
a[n++]=x;
return n>0; //判断是不是空树
}
//L1,R1是中序遍历数组中当前这棵树的起点和终点,L2,R2同理
int build(int L1,int R1,int L2,int R2){
if(L1>R1)
return 0; //临界条件,到了叶子结点根据后面的公式R1将会等于L1-1
int root=post_order[R2]; //当前这棵树的根节点就是后序遍历最后的那个节点
int p=L1; //p用来找并记录根节点在中序遍历中的编号(下标),这样就可以得到左子树的节点数目,进而推算右子树节点数目
while(in_order[p]!=root)
p++;
int cnt=p-L1;//cnt记录左子树的节点数目
lch[root]=build(L1,p-1,L2,L2+cnt-1);
rch[root]=build(p+1,R1,L2+cnt,R2-1);
return root; //返回root的值作为上一层(父节点)的左孩子或者右孩子的值
}
int best,best_sum;//最优的叶子的权值,最优的路径的权值
void dfs(int u,int sum){ //u记录当前节点的值,sum用来记录当前路径上所有路过的节点的权值的和
sum+=u;
if(!lch[u]&&!rch[u])
if(sum<best_sum||(sum==best_sum&& u<best)){ //如果是叶结点那么判断是否比当前最优解更优
best=u;
best_sum=sum;
}
if(lch[u])
dfs(lch[u],sum);
if(rch[u])
dfs(rch[u],sum);
}
int main()
{
while(read_list(in_order)){
read_list(post_order);
build(0,n-1,0,n-1);
best_sum=1000000000;
dfs(post_order[n-1],0);
printf("%d\n",best);
}
return 0;
}