二叉树的建立,二叉树的前序、中序、后续互算,已知二者怎么求第三者,Tree UVA - 548题目分析

这个问题实际上有三个:

  1. 已知二叉树的前序和中序序列,如何计算二叉树的后续序列?
  2. 已知二叉树的中序和后续序列,如何计算二叉树的前序序列?
  3. 已知二叉树的前序和后续序列,如何计算二叉树的中序序列?(这是无法实现的)

更进一步,问题归为一个:

  • 已知二叉树的任意两个遍历序列,如何建立完整的二叉树?

问题一:

已知二叉树的前序和中序序列:

1 2 4 5 8 3 6 9 7 10
4 2 8 5 1 6 9 3 7 10

由前序遍历的性质可知:
  • 任意一个二叉树的前序序列第一个元素是该树的根节点
再由中序遍历的性质可知:
  • 任意一个二叉树的中序序列中,位于根节点左边的元素构成根节点的左子树,位于根节点右边的元素构成根节点的右子树

那么由上面两条性质,可以知道:前序序列判父子关系,中序序列判左右子树,我们可以将前序序列和中序序列各分成三部分来看:第一部分,根节点元素;第二部分,根节点左子树;第三部分,根节点右子树。于是递归就可以进行了。

递归部分:
第一步:确认并记录根节点在PRE数组和IN数组中的位置。在PRE数组中第一个元素PRE[0]就是根节点,遍历IN数组找到等于PRE[0]的元素位置,并记录( 即为图中 i )。
第二步:用 i 的位置将IN分成两部分,记录左子树和右子树的前序和中序序列的开始和结束位置。
第三步:将左子树和右子树进行上述递归。

在这里插入图片描述
下面是实现代码:

int PRE[maxn];// La...Ra
int IN[maxn];// Lb...Rb
Node* build_tree(int La, int Ra, int Lb, int Rb) {
	if (La > Ra || Lb > Rb)return NULL;
	int n = PRE[La];
	int i;
	for (i = Lb;i <= Rb && IN[i] != n;i++);
	int len = i - Lb;
	Node* t = new Node();
	t->data = n;
	t->LChild = build_tree(La + 1, La + len, Lb, i - 1);
	t->RChild = build_tree(La + len + 1, Ra, i + 1, Rb);
	return t;
}

若要得到后续遍历序列,只需要在上面代码 return 语句之前加入:

	printf("%d ", n);

当然也可以重新遍历整个二叉树:

void post_order(Node* root) {
	if (root == NULL)return;
	post_order(root->LChild);
	post_order(root->RChild);
	printf("%d ", root->data);
}

最终得到在这里插入图片描述
下面是完整代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 10000 + 10;
int PRE[maxn], IN[maxn];
struct Node {
	int data;
	Node* LChild, * RChild;
};
Node* build_tree(int La, int Ra, int Lb, int Rb) {
	if (La > Ra || Lb > Rb)return NULL;
	int n = PRE[La];
	int i;
	for (i = Lb;i <= Rb && IN[i] != n;i++);
	int len = i - Lb;
	Node* t = new Node();
	t->data = n;
	t->LChild = build_tree(La + 1, La + len, Lb, i - 1);
	t->RChild = build_tree(La + len + 1, Ra, i + 1, Rb);
	return t;
}
void pre_order(Node* root) {
	if (root == NULL)return;
	printf("%d ", root->data);
	pre_order(root->LChild);
	pre_order(root->RChild);
}
void in_order(Node* root) {
	if (root == NULL)return;
	in_order(root->LChild);
	printf("%d ", root->data);
	in_order(root->RChild);
}
void post_order(Node* root) {
	if (root == NULL)return;
	post_order(root->LChild);
	post_order(root->RChild);
	printf("%d ", root->data);
}
int main() {
	//freopen(".in", "r", stdin);
	//freopen(".out", "w", stdout);
	int N;scanf("%d", &N);
	for (int i = 0;i < N;i++)scanf("%d", PRE + i);
	for (int i = 0;i < N;i++)scanf("%d", IN + i);
	Node* root = build_tree(0, N - 1, 0, N - 1);
	pre_order(root);printf("\n");
	in_order(root);printf("\n");
	post_order(root);printf("\n");
	return 0;
}

结果

1 2 4 5 8 3 6 9 7 10
4 2 8 5 1 6 9 3 7 10
4 8 5 2 9 6 10 7 3 1

问题二:

已知二叉树的中序和后续序列:

3 2 1 4 5 7 6
3 1 2 5 6 7 4

这个问题实质上和上边那个问题是一样的,不同的地方在于问题一的根节点位置在PRE序列的第一个元素,而这个问题里根节点位置在POST序列最后一个。核心思想并未改变。
在这里插入图片描述

int IN[maxn];// La...Ra
int POST[maxn];// Lb...Rb
Node* build_tree(int La, int Ra, int Lb, int Rb) {
	if (La > Ra || Lb > Rb)return NULL;
	int i;
	int n = POST[Rb];
	for (i = La;i <= Ra && n != IN[i];i++);
	int len = i - La;
	Node* t = new Node();
	t->data = n;
	t->LChild = build_tree(La, i - 1, Lb, Lb + len - 1);
	t->RChild = build_tree(i + 1, Ra, Lb + len, Rb - 1);
	return t;
}

下面是一个例题:(Tree UVA - 548

题意:
给出一颗带权树(权值各不相同,且都是小于10000的正整数)的中序和后续遍历方法,找一个叶子节点使从根节点到该叶子节点的路径权值总和最小,若符合条件的叶子节点有多个,输出权值最小的叶子节点。
Sample Input

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

Sample Output

1
3
255

AC代码:

#include <cstdio>
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
using namespace std;
const int maxn = 10000 + 5;
const int inf = 1000000000;
int IN[maxn];
int POST[maxn];
int N;
int ans, SUM;
struct Node {
	int data;
	Node* LChild;
	Node* RChild;
};
Node* build_tree(int La, int Ra, int Lb, int Rb) {
	if (La > Ra || Lb > Rb)return NULL;
	int i;
	int n = POST[Rb];
	for (i = La;i <= Ra && n != IN[i];i++);
	int len = i - La;
	Node* t = new Node();
	t->data = n;
	t->LChild = build_tree(La, i - 1, Lb, Lb + len - 1);
	t->RChild = build_tree(i + 1, Ra, Lb + len, Rb - 1);
	return t;
}

bool read_data(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;
}
void pre_order(Node* root, int sum) {
	if (root == NULL)return;
	sum += root->data;
	if (root->LChild == NULL && root->RChild == NULL) {
		if (SUM > sum || (SUM == sum && root->data < ans)) { SUM = sum;ans = root->data; }
		//printf("sum = %4d  ans = %4d\n", sum, ans);
	}
	pre_order(root->LChild, sum);
	pre_order(root->RChild, sum);
}
int main() {
	//freopen(".in", "r", stdin);
	//freopen(".out", "w", stdout);
	while (read_data(IN)) {
		read_data(POST);
		//for (int i = 0;i < N;i++)printf("%d ", IN[i]);printf("\n");
		//for (int i = 0;i < N;i++)printf("%d ", POST[i]);printf("\n");
		SUM = inf;
		Node* root = build_tree(0, N - 1, 0, N - 1);
		pre_order(root, 0);
		printf("%d\n", ans);
	}
	return 0;
}

问题三:

已知前序和后续序列,能否求出整个二叉树?

图一 图二

上图左图的前序和后续为:

1 2 3 4
4 3 2 1

右图的前序和后续为:

1 2 3 4
4 3 2 1

可见二者虽然不同,但前序和后续序列却是相同的,因此只凭前序和后续序列是无法求出整颗二叉树的。


另外,附上上题顺手写的java代码,java在处理字符串方面比C++好用很多:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.StringTokenizer;

public class Main{
	static class Node{
		int data;
		Node LChild,RChild;
	}
	static int[] IN,POST;
	static final int inf = Integer.MAX_VALUE;
	static int maxn = 10000+10;
	static int N;
	static int SUM,ans;
	public static void main(String[] args) throws IOException {
		//System.setIn(new FileInputStream("in.txt"));
		//System.setOut(new PrintStream("out.txt"));
		Scanner sc = new Scanner(System.in);
		String sa,sb;
		IN = new int[maxn];
		POST = new int[maxn];
		while((sa = sc.nextLine())!=null) {
			sb = sc.nextLine();
			String[] str = sa.split(" +");
			for(int i=0;i<str.length;i++)IN[i] = Integer.valueOf(str[i]);
			str = sb.split(" ");
			for(int i=0;i<str.length;i++)POST[i] = Integer.valueOf(str[i]);
			N = str.length;
			/*for(int i=0;i<N;i++)System.out.printf("%4d",IN[i]);
			System.out.println();
			for(int i=0;i<N;i++)System.out.printf("%4d",POST[i]);*/
			SUM = inf;
			Node root = buildTree(0,N-1,0,N-1);
			preOrder(root,0);
			System.out.println(ans);
		}
	}
	static Node buildTree(int La, int Ra, int Lb, int Rb) {
		if (La > Ra || Lb > Rb)return null;
		int i;
		int n = POST[Rb];
		for (i = La;i <= Ra && n != IN[i];i++);
		int len = i - La;
		Node t = new Node();
		t.data = n;
		t.LChild = buildTree(La, i - 1, Lb, Lb + len - 1);
		t.RChild = buildTree(i + 1, Ra, Lb + len, Rb - 1);
		return t;
	}
	static void preOrder(Node root,int sum) {
		if(root==null)return;
		sum+=root.data;
		if(root.LChild==null&&root.RChild==null) {
			if(SUM>sum||(sum==SUM&&ans>root.data)) {SUM = sum;ans = root.data; }
			return;
		}
		preOrder(root.LChild,sum);
		preOrder(root.RChild,sum);
	}
}
class Scanner 
{
	BufferedReader br;
	StringTokenizer st;
	Scanner(InputStream input)
	{
		br = new BufferedReader(new InputStreamReader (input));
		st = null;
	}
	public String next() throws IOException
	{
		while(st==null || !st.hasMoreTokens())
		{
			String str = br.readLine();
			if(str==null)
				return null;
			st = new StringTokenizer(str);
		}
		return st.nextToken();
	}
	public int nextInt() throws NumberFormatException, IOException
	{
		return Integer.parseInt(next());
	}
	public double nextDouble() throws NumberFormatException, IOException
	{
		return Double.parseDouble(next());
	}
	public Long nextLong() throws NumberFormatException, IOException
	{
		return Long.parseLong(next());
	}
	public String nextLine() throws IOException {
		return br.readLine();
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值