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

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

序列{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. 《Algorithms 4th Edition》读书笔记——2.4 优先队列(priority queue)-Ⅴ

    命题Q.对于一个含有N个元素的基于堆叠优先队列,插入元素操作只需要不超过(lgN + 1)次比较,删除最大元素的操作需要不超过2lgN次比较. 证明.由命题P可知,两种操作都需要在根节点和堆底之间移动 ...

  2. Pythoner | 你像从前一样的Python学习笔记

    Pythoner | 你像从前一样的Python学习笔记 Pythoner

  3. Objective-C中NSString和NSMutableString的基本用法

    int main(int argc, const char * argv[]) { @autoreleasepool { //----------------NSString------------- ...

  4. hdu 5676 ztr loves lucky numbers(dfs+离线)

    Problem Description ztr loves lucky numbers. Everybody knows that positive integers are lucky if the ...

  5. hdu 3635 Dragon Balls(并查集应用)

    Problem Description Five hundred years later, the number of dragon balls will increase unexpectedly, ...

  6. 【网络协议】TCP连接的建立和释放

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/29382883 TCP首部格式 先看TCP报文段的格式,例如以下; TCP报文段首部的前20 ...

  7. 使用dojo的tree

    dojo的Tree非常是灵活,可是官方站点上的样例却非常少,并且也比較分散,兴许将持续完好本样例. 总的来说,要使用tree,要接触到三个类:"dojo/store/JsonRest&quo ...

  8. Javascript进阶篇——( JavaScript内置对象---下)--Array数组对象---笔记整理

    Array 数组对象数组对象是一个对象的集合,里边的对象可以是不同类型的.数组的每一个成员对象都有一个“下标”,用来表示它在数组中的位置,是从零开始的数组定义的方法: 1. 定义了一个空数组: var ...

  9. OutputCache各参数的说明

    OutputCache各参数的说明 Duration 缓存时间,以秒为单位,这个除非你的Location=None,可以不添加此属性,其余时候都是必须的. Location Location当被设置为 ...

  10. 软件测试作业三 尝试使用JUnit

    写一个判断三角形种类的代码,对其进行测试. 判断三角形代码: package 测试1; public class sjx { public String f(int a,int b,int c) { ...