剑指Offer面试题6:重建二叉树 Java实现

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

算法分析:
在二叉树的前序遍历序列中,第一个数字总是树的根节点的值。但在中序遍历中,根节点的值在序列的中间,左子树的结点的值位于根节点的值的左边,而右子树的结点的值位于根节点的右边。因此我们需要扫描中序遍历序列,才能找到根节点的值。
 
如图所示,前序遍历序列的第一个数字1就是根节点的值。扫描中序遍历序列,就能确定根节点的值的位置。根据中序遍历的特点,在根节点的值1前面3个数字都是左子树结点的值,位于1后面的数字都是右子树结点的值。
 
由于中序遍历序列中,有3个数字是左子树结点的值,因此左子树总共有3个左子结点。同样,在前序遍历的序列中,根节点后面的3个数字就是3个左子树结点的值,再后面的所有数字都是右子树结点的值。这样我们就在前序遍历和中序遍历两个序列中,分别找到了左右子树对应的子序列。
 
 
既然我们已经分别找到了左、右子树的前序遍历序列和中序遍历序列,我们可以用同样的方法分别去构建左右子树。也就是说,接下来的事情可以用递归的方法去完成。


算法源程序;

   
   
  1. /**************************************************************
  2. * Copyright (c) 2016, 北京邮电大学
  3. * All rights reserved.
  4. * 版 本 号:v1.0
  5. * 题目描述:重建二叉树
  6. * 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复数字。
  7. * 例如,输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出图中所示的二叉树并输出它的头节点。
  8. * 输入描述:无
  9. * 程序输出: 4 7 2 1 5 3 8 6 Exception in thread "main"
  10. * 5 4 3 2 1
  11. * 1 2 3 4 5
  12. * 1
  13. * 4 2 5 1 6 3 7
  14. * 问题分析:无
  15. * 算法描述:无
  16. * 完成时间:2016-10-30
  17. ***************************************************************/
  18. package org.marsguo.offerproject06;
  19. /*
  20. 定义二叉树节点
  21. */
  22. class TreeNode{
  23. int val;
  24. public TreeNode left = null;
  25. public TreeNode right = null;
  26. }
  27. class SolutionMethod1{
  28. /**
  29. * 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二节树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
  30. *
  31. * @param preorder 前序遍历
  32. * @param inorder 中序遍历
  33. * @return 树的根结点
  34. */
  35. public TreeNode BinaryTreeFunction(int[] preorder,int[] inorder){
  36. if(preorder == null || inorder == null ||
  37. preorder.length != inorder.length || inorder.length < 1){
  38. return null;
  39. }
  40. return ConstructCore(preorder,0,preorder.length - 1,inorder,0,inorder.length - 1);
  41. }
  42. /**
  43. * 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二节树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
  44. *
  45. * @param preorder 前序遍历
  46. * @param ps 前序遍历的开始位置
  47. * @param pe 前序遍历的结束位置
  48. * @param inorder 中序遍历
  49. * @param is 中序遍历的开始位置
  50. * @param ie 中序遍历的结束位置
  51. * @return 树的根结点
  52. */
  53. public TreeNode ConstructCore(int[] preorder, int ps, int pe, int[] inorder, int is, int ie){
  54. if(ps > pe){ //开始位置大于结束位置,说明已经没有需要处理的元素了
  55. return null;
  56. }
  57. int value = preorder[ps]; // 取前序遍历的第一个数字,就是当前的根结点
  58. int index = is;
  59. while(index <= ie && inorder[index] != value){ // 在中序遍历的数组中找根结点的位置
  60. index++;
  61. }
  62. if(index > ie){ // 如果在整个中序遍历的数组中没有找到,说明输入的参数是不合法的,抛出异常
  63. throw new RuntimeException("Invalid input");
  64. }
  65. TreeNode node = new TreeNode(); // 创建当前的根结点,并且为结点赋值
  66. node.val = value;
  67. /*
  68. 递归构建当前根结点的左子树,左子树的元素个数:index-is+1个
  69. 左子树对应的前序遍历的位置在[ps+1, ps+index-is]
  70. 左子树对应的中序遍历的位置在[is, index-1]
  71. */
  72. node.left = ConstructCore(preorder, ps + 1, ps + index - is, inorder, is, index - 1);
  73. /*递归构建当前根结点的右子树,右子树的元素个数:ie-index个
  74. 右子树对应的前序遍历的位置在[ps+index-is+1, pe]
  75. 右子树对应的中序遍历的位置在[index+1, ie]
  76. */
  77. node.right = ConstructCore(preorder, ps + index - is + 1, pe, inorder, index + 1, ie);
  78. // 返回创建的根结点
  79. return node;
  80. }
  81. public void printTree(TreeNode root){ // 中序遍历二叉树
  82. if(root != null){
  83. printTree(root.left);
  84. System.out.print(root.val + " ");
  85. printTree(root.right);
  86. }
  87. //System.out.println();
  88. }
  89. // 普通二叉树
  90. // 1
  91. // / \
  92. // 2 3
  93. // / / \
  94. // 4 5 6
  95. // \ /
  96. // 7 8
  97. public void test1() {
  98. int[] preorder = {1, 2, 4, 7, 3, 5, 6, 8};
  99. int[] inorder = {4, 7, 2, 1, 5, 3, 8, 6};
  100. TreeNode root = BinaryTreeFunction(preorder, inorder);
  101. printTree(root);
  102. }
  103. // 所有结点都没有右子结点
  104. // 1
  105. // /
  106. // 2
  107. // /
  108. // 3
  109. // /
  110. // 4
  111. // /
  112. // 5
  113. public void test2() {
  114. int[] preorder = {1, 2, 3, 4, 5};
  115. int[] inorder = {5, 4, 3, 2, 1};
  116. TreeNode root = BinaryTreeFunction(preorder, inorder);
  117. printTree(root);
  118. }
  119. // 所有结点都没有左子结点
  120. // 1
  121. // \
  122. // 2
  123. // \
  124. // 3
  125. // \
  126. // 4
  127. // \
  128. // 5
  129. public void test3() {
  130. int[] preorder = {1, 2, 3, 4, 5};
  131. int[] inorder = {1, 2, 3, 4, 5};
  132. TreeNode root = BinaryTreeFunction(preorder, inorder);
  133. printTree(root);
  134. }
  135. // 树中只有一个结点
  136. public void test4() {
  137. int[] preorder = {1};
  138. int[] inorder = {1};
  139. TreeNode root = BinaryTreeFunction(preorder, inorder);
  140. printTree(root);
  141. }
  142. // 完全二叉树
  143. // 1
  144. // / \
  145. // 2 3
  146. // / \ / \
  147. // 4 5 6 7
  148. public void test5() {
  149. int[] preorder = {1, 2, 4, 5, 3, 6, 7};
  150. int[] inorder = {4, 2, 5, 1, 6, 3, 7};
  151. TreeNode root = BinaryTreeFunction(preorder, inorder);
  152. printTree(root);
  153. }
  154. // 输入空指针
  155. public void test6() {
  156. BinaryTreeFunction(null, null);
  157. }
  158. // 输入的两个序列不匹配
  159. public void test7() {
  160. int[] preorder = {1, 2, 4, 5, 3, 6, 7};
  161. int[] inorder = {4, 2, 8, 1, 6, 3, 7};
  162. TreeNode root = BinaryTreeFunction(preorder, inorder);
  163. printTree(root);
  164. }
  165. }
  166. public class RebuildBinaryTree {
  167. public static void main(String[] args){
  168. SolutionMethod1 solution1 = new SolutionMethod1();
  169. solution1.test1();
  170. System.out.println();
  171. solution1.test2();
  172. System.out.println();
  173. solution1.test3();
  174. System.out.println();
  175. solution1.test4();
  176. System.out.println();
  177. solution1.test5();
  178. System.out.println();
  179. solution1.test6();
  180. System.out.println();
  181. solution1.test7();
  182. System.out.println();
  183. }
  184. }


程序运行结果

 
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值