题目:输入某二叉树的前序遍历和中序遍历结果,请重建出该二叉树。假设

输入的前序遍历和中序遍历的结果都不含重复的数字。例如输入前序遍历

序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如图

所示的二叉树并输出它的头结点。二叉树的定义如下:

 struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight;
}
                /     \

              /        / \

             \          /
                        

首先要明白一些基本概念:前序遍历,中序遍历,后续遍历

以上图中的二叉树为例:

前序遍历:我的理解是 “根-左-右”  先访问根结点 再访问左子树 最好访问右子树

比如:先访问根结点1,再访问左子节点,左子节点为一颗子树,那么又用根-左-右

的规则,先访问根节点2,再访问左在节点4,再访问右子节点。这样知道所有

节点遍历结束:那么遍历的节点列表是:1,2,4,7,3,5,6,8

中序遍历遵循规则:“左-根-右”  遍历的节点列表是:4,7,2,1,5,3,8,6

后序遍历遵循规则:“左-右-根”  遍历的节点列表是:7,4,2,5,8,6,3,1

那么从另外一个方面来说:任何一个二叉树三种遍历方式的结果是唯一。

反之也就是说这三种遍历结果唯一的确定一颗二叉树。

其实当我们分析会发现实际上在唯一确定一个二叉树的时候并非

完全给出三种方式的遍历结果,实际上我们只需要前序遍历与

中序遍历 又或者中序遍历与后序遍历。注意,如果仅仅是先序

遍历和后序遍历是不能唯一确定一个二叉树的。这点是需要特别注意的

下面我们来分析一下:

如果我们只知道一个二叉树的中序遍历:4,7,2,1,5,3,8,6

与后序遍历:7,4,2,5,8,6,3,1

现在我们开分析一下能不能通过这两中遍历结果推出先序

遍历结果:1,2,4,7,3,5,6,8

我们知道后序遍历可知,遍历列表最后一个元素为二叉树的根结点

可知该最后一个元素为1,那么跟结点为1.

在中序遍历结果中找到根节点所在的位置,那么该位置前面的即为

二叉树的左子树:4,7,2    该位置后面的为二叉树的右子树:5,3,8,6

那么我们可以通过找到的左右子树确定后序遍历的左右子树遍历结果:

7,4,2和5,8,6,3  

现在我们又分别针对每个子树重复上述步骤:显然前序遍历结果为:1,2,4,7,3,5,8,6

很显然前序与中序结果退出后序遍历结果的步骤是一样一样的。

那么问题来了,为什么不能通道前序和后序遍历结果推出中序遍历结果呢?

因为我们不能通过前序和中序遍历确定遍历结果中关于根节点的左右子树。

也就是说前序和后序遍历结果重建的二叉树不一定是唯一的。

好了有了前面的分析我们不难通过递归的方式解决这道题,

剑指Offer书上是通过前序和中序遍历推出后序遍历()

代码如下:

 #include <iostream>
using namespace std; struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight;
}; BinaryTreeNode* ConstructCore(int* startPreorder,int* endPreorder,int*startInorder,int* endInorder)
{
int rootValue = startPreorder[];
struct BinaryTreeNode* root = (struct BinaryTreeNode*)malloc(sizeof(struct BinaryTreeNode));
root->m_nValue=rootValue;
root->m_pLeft=NULL;
root->m_pRight=NULL; if(startPreorder == endPreorder)
{
if(startInorder == endInorder&& *startPreorder==*startInorder)
return root;
else
throw exception("Invalid Input.");
} int* rootInorder=startInorder;
while(rootInorder<=endInorder&&*rootInorder!=rootValue)
++rootInorder; if(rootInorder==endInorder&&*rootInorder!=rootValue)
throw exception("Invalid Input."); int leftLength = rootInorder-startInorder;
int* LeftPreorderEnd=startPreorder+leftLength;
if(leftLength>)
{
root->m_pLeft=ConstructCore(startPreorder+,LeftPreorderEnd,startInorder,rootInorder-);
} if(leftLength<endPreorder-startPreorder)
{
root->m_pRight=ConstructCore(LeftPreorderEnd+,endPreorder,rootInorder+,endInorder);
} return root;
} BinaryTreeNode* Construct(int* preorder,int* inorder,int length)
{
if(preorder==NULL||inorder==NULL||length<=)
return NULL;
return ConstructCore(preorder,preorder+length-,inorder,inorder+length-);
} void AfterOrderPrint(struct BinaryTreeNode* root)
{
if(root)
{
AfterOrderPrint(root->m_pLeft);
AfterOrderPrint(root->m_pRight);
cout<<root->m_nValue<<" ";
}
} int main()
{
BinaryTreeNode *Root;
int preList[]={,,,,,,,};
int inList[]={,,,,,,,};
Root=Construct(preList,inList,);
AfterOrderPrint(Root);
cout<<endl;
return ;
}

截图:

 对的吧

接下来我们模仿剑指offer书中的上述代码,通过前后序遍历和中序遍历

推出前序遍历进而重建这颗二叉树。

 #include <iostream>
using namespace std; struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight;
}; BinaryTreeNode* ConstructCore(int* startAfterorder,int* endAfterorder,int*startInorder,int* endInorder)
{
int rootValue = endAfterorder[];
struct BinaryTreeNode* root = (struct BinaryTreeNode*)malloc(sizeof(struct BinaryTreeNode));
root->m_nValue=rootValue;
root->m_pLeft=NULL;
root->m_pRight=NULL; int* rootInorder=startInorder;
while(rootInorder<=endInorder&&*rootInorder!=rootValue)
++rootInorder; int leftLength = rootInorder-startInorder;
int* LeftAfterorderEnd=startAfterorder+leftLength;
if(leftLength>)
{
root->m_pLeft=ConstructCore(startAfterorder,LeftAfterorderEnd-,startInorder,rootInorder-);
} if(leftLength<endAfterorder-startAfterorder)
{
root->m_pRight=ConstructCore(LeftAfterorderEnd,endAfterorder-,rootInorder+,endInorder);
} return root;
} BinaryTreeNode* Construct(int* Afterorder,int* inorder,int length)
{
if(Afterorder==NULL||inorder==NULL||length<=)
return NULL;
return ConstructCore(Afterorder,Afterorder+length-,inorder,inorder+length-);
} void PreOrderPrint(struct BinaryTreeNode* root)
{
if(root)
{
cout<<root->m_nValue<<" ";
PreOrderPrint(root->m_pLeft);
PreOrderPrint(root->m_pRight);
}
} int main()
{
BinaryTreeNode *Root;
int AfterList[]={,,,,,,,};
int inList[]={,,,,,,,};
Root=Construct(AfterList,inList,);
PreOrderPrint(Root);
cout<<endl;
return ;
}

咦 正确了哦

两点说明:

1.使用前序中序以及中序后序重建一个二叉树,这颗二叉树中的节点元素值必须完全不相等。

为什么呢,假设除开根节点外还有另外一个节点值为1 那么在前序或者后序遍历中怎么能

确定这个节点左子树和右子树的分界点呢,既然不能确定,那么显然不能递归重建一个唯一的

二叉树

2.注意关于前序中序重建二叉树时应该注意前序遍历与中序遍历的一致性,否则无法重建这可二叉树。

剑指offer-面试题6.重建二叉树的更多相关文章

  1. 剑指offer面试题6 重建二叉树(c)

  2. 剑指offer面试题6 重建二叉树(java)

    注:(1)java中树的构建 (2)构建子树时可以直接利用Arrays.copyOfRange(preorder, from, to),这个方法是左开右闭的 package com.xsf.SordF ...

  3. 剑指Offer:面试题6——重建二叉树(java实现)

    问题描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不包含重复的数字. 例如: 输入:前序{1,2,4,7,3,5,6,8},中序{4,7,2,1 ...

  4. C++版 - 剑指Offer 面试题39:二叉树的深度(高度)(二叉树深度优先遍历dfs的应用) 题解

    剑指Offer 面试题39:二叉树的深度(高度) 题目:输入一棵二叉树的根结点,求该树的深度.从根结点到叶结点依次经过的结点(含根.叶结点)形成树的一条路径,最长路径的长度为树的深度.例如:输入二叉树 ...

  5. 剑指Offer - 九度1385 - 重建二叉树

    剑指Offer - 九度1385 - 重建二叉树2013-11-23 23:53 题目描述: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的 ...

  6. 剑指offer_面试题6_重建二叉树(分解步骤,逐个击破)

    题目:输入某二叉树的前序遍历和中序遍历的结果.请重建出该二叉树.如果输入的前序遍历和中序遍历的结果中都不含反复的数字. 比如:输入前序遍历 {1,2,4,7,3,5,6,8} 和中序遍历序列 {4,7 ...

  7. 剑指offer第二版-7.重建二叉树

    描述:输入某二叉树的前序遍历和中序遍历结果,重建该二叉树.假设前序遍历或中序遍历的结果中无重复的数字. 思路:前序遍历的第一个元素为根节点的值,据此将中序遍历数组拆分为左子树+root+右子树,前序遍 ...

  8. 剑指offer【04】- 重建二叉树(java)

    题目:重建二叉树 考点:树 题目描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6, ...

  9. 剑指offer(4)重建二叉树

    题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7, ...

  10. 剑指offer——面试题8:二叉树的下一个节点

    // 面试题8:二叉树的下一个结点 // 题目:给定一棵二叉树和其中的一个结点,如何找出中序遍历顺序的下一个结点? // 树中的结点除了有两个分别指向左右子结点的指针以外,还有一个指向父结点的指针. ...

随机推荐

  1. 多系统实现单点登录方案:SSO 单点登录

    一.什么是单点登录SSO(Single Sign-On) SSO是一种统一认证和授权机制,指访问同一服务器不同应用中的受保护资源的同一用户,只需要登录一次,即通过一个应用中的安全验证后,再访问其他应用 ...

  2. 《Java程序员面试笔试宝典》之组合与继承有什么区别

    组合和继承是面向对象中两种代码复用的方式.组合是指在新类里面创建原有类的对象,重复利用已有类的功能.继承是面向对象的主要特性之一,它允许设计人员根据其它类的实现来定义一个类的实现.组合和继承都允许在新 ...

  3. QT绘制系统简介

    #3个类:QPainter,QPainterDevice 和 QPaintEngine 三个类 #qpainter用于执行绘制操作 #QPainterDevice是一个二维空间抽象,允许qpainte ...

  4. Biorhythms(中国剩余定理)

    Biorhythms Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 127339   Accepted: 40342 Des ...

  5. HashMap的使用方法及注意事项

    99.Map(映射):Map 的keySet()方法会返回 key 的集合,因为 Map 的键是不能重复的,因此 keySet()方法的返回类型是 Set:而 Map 的值是可以重复的,因此 valu ...

  6. [Hapi.js] Request Validation with Joi

    hapi supports request validation out of the box using the joi module. Request path parameters, paylo ...

  7. Servlet实现Session

    (1)首先看一下项目的结构 是在tomcat--webaps下的myWebSites项目 在myWebSites下有仅仅有WEB-INF目录 在WEB-INF目录中有  一下目录(在classes目录 ...

  8. java 中Date的格式化样式

    public static void main(String[] args) { Date d = new Date(); System.out.println(d); // Date类的默认格式 T ...

  9. [IDEA学习笔记][keymap]

    一个总站: http://www.youmeek.com/ 常用的快捷键keymap 提示: ctrl+N:快速打开一个类 Ctrl+P 方法参数提示显示 Ctrl+J 提示自定义模板 Ctrl+O ...

  10. URI, URL, and URN

    URI: uniform resource identifier,统一资源标识符,用来唯一的标识一个资源. URL: uniform resource locator,统一资源定位器,它是一种具体的U ...