一、题目:重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示的二叉树并输出它的头结点。 

二、解题思路

  在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。因此我们需要扫描中序遍历序列,才能找到根结点的值。

  前序遍历序列的第一个数字1就是根结点的值。扫描中序遍历序列,就能确定根结点的值的位置。根据中序遍历特点,在根结点的值1前面的3个数字都是左子树结点的值,位于1后面的数字都是右子树结点的值。

  在二叉树的前序遍历和中序遍历的序列中确定根结点的值、左子树结点的值和右子树结点的值的步骤如下图所示:

  分别找到了左、右子树的前序遍历序列和中序遍历序列,我们就可以用同样的方法分别去构建左右子树。换句话说,这是一个递归的过程。

思路总结:先根据前序遍历序列的第一个数字创建根结点,接下来在中序遍历序列中找到根结点的位置,这样就能确定左、右子树结点的数量。在前序遍历和中序遍历的序列中划分了左、右子树结点的值之后,就可以递归地去分别构建它的左右子树。

三、解决问题

3.1 代码实现

    public static Node<int> Construct(int[] preOrder, int[] inOrder, int length)
{
// 空指针判断
if (preOrder == null || inOrder == null || length <= )
{
return null;
} return ConstructCore(preOrder, , preOrder.Length - , inOrder, , inOrder.Length - );
} public static Node<int> ConstructCore(int[] preOrder, int startPreOrder, int endPreOrder, int[] inOrder, int startInOrder, int endInOrder)
{
// 前序遍历序列的第一个数字是根结点的值
int rootValue = preOrder[startPreOrder];
Node<int> root = new Node<int>();
root.data = rootValue;
root.lchild = root.rchild = null; if (startPreOrder == endPreOrder)
{
if (startInOrder == endInOrder &&
preOrder[startPreOrder] == inOrder[startInOrder])
{
return root;
}
else
{
throw new Exception("Invalid input!");
}
} // 在中序遍历中找到根结点的值
int rootInOrder = startInOrder;
while (rootInOrder <= endInOrder && inOrder[rootInOrder] != rootValue)
{
rootInOrder++;
} // 输入的两个序列不匹配的情况
if (rootInOrder == endInOrder && inOrder[rootInOrder] != rootValue)
{
throw new Exception("Invalid input!");
} int leftLength = rootInOrder - startInOrder;
int leftPreOrderEnd = startPreOrder + leftLength;
if (leftLength > )
{
// 构建左子树
root.lchild = ConstructCore(preOrder, startPreOrder + , leftPreOrderEnd, inOrder, startInOrder, rootInOrder - );
}
if (leftLength < endPreOrder - startPreOrder)
{
// 构建右子树
root.rchild = ConstructCore(preOrder, leftPreOrderEnd + , endPreOrder, inOrder, rootInOrder + , endInOrder);
} return root;
}

3.2 单元测试

  首先封装了一个测试主入口,方法定义如下:

    // 单元测试主入口
public static void ConstructTestPortal(string testName, int[] preOrder, int[] inOrder, int length)
{
if (!string.IsNullOrEmpty(testName))
{
Console.WriteLine("{0} begins:", testName);
}
// 打印先序遍历
Console.Write("The preorder sequence is : ");
for (int i = ; i < length; i++)
{
Console.Write(preOrder[i]);
}
Console.Write("\n");
// 打印中序遍历
Console.Write("The inorder sequence is : ");
for (int i = ; i < length; i++)
{
Console.Write(inOrder[i]);
}
Console.Write("\n"); try
{
Node<int> root = Construct(preOrder, inOrder, length);
BinaryTree<int> bTree = new BinaryTree<int>();
bTree.Root = root;
Console.Write("The binary tree is : ");
// 重建的二叉树层次遍历
bTree.LevelOrder(bTree.Root);
if (!string.IsNullOrEmpty(testName))
{
Console.Write("\n{0} end\n\n", testName);
}
}
catch (Exception)
{
Console.WriteLine("Invalid input!");
}
}

  该方法首先接收参数,依次打印先序遍历和中序遍历,最后通过调用Construct方法获得重建的二叉树的根节点,并实例化一个二叉树的数据结构(本处的二叉树结构的实现请阅读《数据结构基础温故-4.树与二叉树(上)》),最后输出重建后的二叉树的层次遍历验证是否重建成功。

  (1)普通二叉树

    // 普通二叉树
// 1
// / \
// 2 3
// / / \
// 4 5 6
// \ /
// 7 8
public static void ConstructTest1()
{
int[] preorder = { , , , , , , , };
int[] inorder = { , , , , , , , }; ConstructTestPortal("ConstructTest1", preorder, inorder, );
}

  (2)所有结点都没有右子结点

    // 所有结点都没有右子结点
// 1
// /
// 2
// /
// 3
// /
// 4
// /
//
public static void ConstructTest2()
{
int[] preorder = { , , , , };
int[] inorder = { , , , , }; ConstructTestPortal("ConstructTest2", preorder, inorder, );
}

  (3)所有结点都没有左子结点

    // 所有结点都没有左子结点
// 1
// \
// 2
// \
// 3
// \
// 4
// \
// 5
public static void ConstructTest3()
{
int[] preorder = {, , , , };
int[] inorder = {, , , , }; ConstructTestPortal("ConstructTest3", preorder, inorder, );
}

  (4)树中只有一个结点

    // 树中只有一个结点
public static void ConstructTest4()
{
int[] preorder = { };
int[] inorder = { }; ConstructTestPortal("ConstructTest4", preorder, inorder, );
}

  (5)完全二叉树

    // 完全二叉树
// 1
// / \
// 2 3
// / \ / \
// 4 5 6 7
public static void ConstructTest5()
{
int[] preorder = {, , , , , , };
int[] inorder = {, , , , , , }; ConstructTestPortal("ConstructTest5", preorder, inorder, );
}

  (6)输入空指针

    // 输入空指针
public static void ConstructTest6()
{
ConstructTestPortal("ConstructTest6", null, null, );
}

  (7)输入的两个序列不匹配

    // 输入的两个序列不匹配
public static void ConstructTest7()
{
int[] preorder = {, , , , , , };
int[] inorder = {, , , , , , }; ConstructTestPortal("ConstructTest7", preorder, inorder, );
}

  单元测试的结果如下图所示:

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

剑指Offer面试题:5.重建二叉树的更多相关文章

  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. 【Java EE 学习 72 下】【数据采集系统第四天】【移动/复制页分析】【使用串行化技术实现深度复制】

    一.移动.复制页的逻辑实现 移动.复制页的功能是在设计调查页面的时候需要实现的功能.规则是如果在同一个调查中的话就是移动,如果是在不同调查中的就是复制. 无论是移动还是复制,都需要注意一个问题,那就是 ...

  2. Eclipse 调试Bug之使用断点的七大技巧

    原文链接:http://xiaohuafyle.iteye.com/blog/1705494 调试竟然有这么多技巧,亏我以前竟不知道...   Eclipse这个开发工具为我们调试bug提供了非常便利 ...

  3. Z-STACK在CC2530上同时使用两个串口

    定义: 1.同时使用两个串口必须要一个为DMA,一个为ISR 2.我们这里使用串口1(DMA)来和别的设备进行通讯,使用 2(ISR)来和Z-TOOL进行通讯,方便调试 HAL_UART=TRUE Z ...

  4. Beta工作比例(Transcend)

    Beta工作比例 成员 工作 黄志明 10% 洪志兴 10% 李佳恺 17 % 巫振格 17 % 肖承志 10 % 李严 16 % 牛妍辉 stripes 20%

  5. 【微信开发】—7200s限制,非数据库

    两种微信缓存方式(7200s) 第一种是MemoryCache(缓存的分享票据) public static string Getjsapi_ticket(string AppID, string A ...

  6. DP套DP HDOJ 4899 Hero meet devil(国王的子民的DNA)

    题目链接 题意: 给n长度的S串,对于0<=i<=|S|,有多少个长度为m的T串,使得LCS(S,T) = i. 思路: 理解的不是很透彻,先占个坑. #include <bits/ ...

  7. Cucumber(一): Preparation

    Every time I wrote some code in ruby and executed our cucumber features I craved for something simil ...

  8. Sqoop2中传入配置文件中url之【坑】

    [特别注意]Sqoop2里面各个版本的区别还是很大的,这里使用1.99.6版本. sqoop2的url等信息放到properties配置文件中,配置文件解析出来传给SqoopClient报错. Sqo ...

  9. java并发J.U.C AtomicReference VS Volatile

    SpinLock实现,摘自并发编程网 package com.juc.simple; import java.util.concurrent.atomic.AtomicReference; /** * ...

  10. [Noip2016]蚯蚓 D2 T2 队列

    [Noip2016]蚯蚓 D2 T2 Description 本题中,我们将用符号[c]表示对c向下取整,例如:[3.0」= [3.1」=[3.9」=3.蛐蛐国最近蚯蚓成灾了!隔壁跳 蚤国的跳蚤也拿蚯 ...