【剑指Offer面试题】 九度OJ1385:重建二叉树

题目链接地址:
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: 1385
    Language: C++
    Result: Accepted
    Time:10 ms
    Memory:1552 kb
****************************************************************/
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值