题目链接地址:
http://ac.jobdu.com/problem.php?pid=1385
题目1385:重建二叉树
时间限制:1 秒内存限制:32 兆特殊判题:否提交:4441解决:1321
题目描写叙述:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含反复的数字。比如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并输出它的后序遍历序列。
输入:
输入可能包括多个測试例子,对于每一个測试案例,
输入的第一行为一个整数n(1<=n<=1000):代表二叉树的节点个数。
输入的第二行包括n个整数(当中每一个元素a的范围为(1<=a<=1000)):代表二叉树的前序遍历序列。
输入的第三行包括n个整数(当中每一个元素a的范围为(1<=a<=1000)):代表二叉树的中序遍历序列。
输出:
相应每一个測试案例,输出一行:
假设题目中所给的前序和中序遍历序列能构成一棵二叉树。则输出n个整数。代表二叉树的后序遍历序列,每一个元素后面都有空格。
假设题目中所给的前序和中序遍历序列不能构成一棵二叉树,则输出”No”。
例子输入:
8
1 2 4 7 3 5 6 8
4 7 2 1 5 3 8 6
8
1 2 4 7 3 5 6 8
4 1 2 7 5 3 8 6
例子输出:
7 4 2 5 8 6 3 1
No
分析:
採用递归重构二叉树
(左代表左子树。右代表右子树,根代表根结点)
前序遍历:根-左-右
中序遍历:左-根-右
后序遍历:左-右-根
定理:给定某棵二叉树的中序遍历序列和前序遍历序列(或者后序遍历序列)就能唯一构造出该二叉树。
原因——由于通过前序遍历(或者后序遍历)能够找到二叉树的根结点。再依据根结点在中序遍历序列中的位置就能够确定根结点的左右子树。由于二叉树是一种递归结构,二叉树的左右子树也都是二叉树,所以递归依据前序和中序遍历序列能够确定各个结点之间的父子关系。
採用递归的思路将问题转化为本质同样可是规模更小的子问题。
前序遍历的第一个节点为根节点,依据根节点的值在中序遍历中找到其相应位置。左边是左子树,右边是右子树。然后左右递归求解就可以。
须要注意的是:假设中序遍历序列不包括前序遍历序列第一个元素则表明无法重构二叉树。
时间复杂度:
每次在中序遍历中找根节点的位置须要O(n)的查找时间,推导复杂度:
T(n) = 2 * T(n / 2) + O(1) + O(n)
T(n) = O(n * log(n))
空间复杂度
递归求解,由于每一个节点都会被递归到。所以空间复杂度为O(n)。
代码:
/*********************************
-----------------------------------
【剑指Offer面试题】 九度OJ1385:重建二叉树
-----------------------------------
Author:牧之丶 Date:2015年
Email:bzhou84@163.com
**********************************/
#include<stdio.h>
#include <iostream>
using namespace std;
#define MAX 1005// 二叉树的结点
typedef struct Node
{int data; // 数据域Node * lChild; // 左子树Node * rChild; // 右子树
}BTNode;
BTNode bTNode[MAX];bool RebuildBinaryTree; // 推断是否能重构二叉树
int preOrder[MAX];
int inOrder[MAX]; // 初始化二叉树中的每一个结点
void initBinaryTree(int n)
{int i;RebuildBinaryTree = true;for(i = 0;i < n;i++){bTNode[i].data = preOrder[i]; bTNode[i].lChild = NULL;bTNode[i].rChild = NULL;}
}//重构二叉树
void reBuildBinaryTree(int beginPreOrder,int endPreOrder,int beginInOrder,int endInOrder)
{int i;int position = -1; // 前序遍历序列第一个结点在中序遍历序列中的位置bool flag = false; // 推断前序遍历序列中的第一个结点是否在中序遍历序列中for(i = beginInOrder;i <= endInOrder;i++) // 遍历二叉树的中序遍历序列,得到根结点在中序遍历序列中的位置{if(preOrder[beginPreOrder] == inOrder[i]){position = i - beginInOrder;flag = true;break;}}if(false == flag){RebuildBinaryTree = false;return;}else{//重构左子树if(beginPreOrder + 1 <= beginPreOrder + position && beginInOrder <= beginInOrder + position - 1){bTNode[beginPreOrder].lChild = &bTNode[beginPreOrder + 1];reBuildBinaryTree(beginPreOrder + 1,beginPreOrder + position,beginInOrder,beginInOrder + position - 1);}//重构右子树if(beginPreOrder + position + 1 <= endPreOrder && beginInOrder + position + 1 <= endInOrder){bTNode[beginPreOrder].rChild = &bTNode[beginPreOrder + position + 1];reBuildBinaryTree(beginPreOrder + position + 1,endPreOrder,beginInOrder + position + 1,endInOrder);}}
}//后序遍历输出
void postOrder(BTNode * root)
{if(NULL == root)return;else{postOrder(root -> lChild);postOrder(root -> rChild);cout<<root -> data<<" ";}
}int main()
{int n;while(cin>>n){for(int i = 0;i < n;i++){scanf("%d",&preOrder[i]);}for(int i = 0;i < n;i++){scanf("%d",&inOrder[i]);}initBinaryTree(n);reBuildBinaryTree(0,n - 1,0,n - 1);if(false == RebuildBinaryTree)cout<<"No"<<endl;else{postOrder(&bTNode[0]); //bTNode[0]是根结点cout<<endl;}}return 0;
}/**************************************************************Problem: 1385Language: C++Result: AcceptedTime:10 msMemory:1552 kb
****************************************************************/