13 Feb 2018
重建二叉树
一、题目
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如:前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},重建二叉树并输出它的头结点。
二、解题思路
Notzuonotdied的结题思路
由前序遍历的第一个节点可知根节点。根据根节点,可以将中序遍历划分成左右子树。在前序遍历中找出对应的左右子树,其第一个节点便是根节点的左右子节点。按照上述方式递归便可重建二叉树。
完整思路:
- 首先温习下树的遍历过程,二叉树的三种遍历可以简化为:
- 前序遍历:根左右;
- 中序遍历:左根右;
- 后序遍历:左右根。
- 上面给出的遍历序列,
- 从前序遍历序列{1,2,4,7,3,5,6,8}中,我们可以依据“根左右”得出根结点是1。
- 从中序遍历序列{4,7,2,1,5,3,8,6},我们可以依据根结点1得出:该树的左子树是{4,7,2},右子树是{5,3,8,6}。
- 根结点是1,根结点的左子树LT为:
- 前序遍历序列为{2,4,7}
- 中序遍历序列为{4,7,2}
- 因此,LT的根结点是2,左子树为{4,7},没有右子树。
- 同理,可以得出4是LT的左子树根结点,且该根结点不存在左子树,右子树为7。
- 根结点为1,根结点的右子树RT为:
- 前序遍历序列为{3,5,6,8}
- 中序遍历序列为{5,3,8,6}
- 因此,RT的根结点为3,左子树为5,右子树RRT为8和6。
- 同理,可以得出RRT的根结点为6,左子树为8,没有右子树。
- 好啦,到这里,我们的分析就结束了。
实现代码
fun main(args: Array<String>) {
// 注意:这里假设不会存在相同的value的结点,可以采用key(hashValue)-value解决。
val preOrder = intArrayOf(1, 2, 4, 7, 3, 5, 6, 8)
val inOrder = intArrayOf(4, 7, 2, 1, 5, 3, 8, 6)
// 恢复二叉树
val node = recoverBinaryTree(preOrder = preOrder, inOrder = inOrder)
// 前序遍历
preOrder(node)
// 中序遍历
inOrder(node)
// 后序遍历
postOrder(node)
}
/**
* 通过二叉树的前序遍历和中序遍历重建二叉树
*
* @param preOrder 前序遍历序列
* @param inOrder 中序遍历序列
* @return 返回根结点
* */
fun recoverBinaryTree(preOrder: IntArray, inOrder: IntArray): BinaryTreeNode? {
// 如果preOrder和inOrder的大小为空,就直接返回
if (preOrder.isEmpty() || inOrder.isEmpty() || preOrder.size != inOrder.size) return null
/**
* 重建
*
* @param preOrder 前序遍历序列
* @param pStart 前序遍历序列的起始位置
* @param pEnd 前序遍历序列的结束位置
* @param inOrder 中序遍历序列
* @param iStart 中序遍历序列的起始位置
* @param iEnd 中序遍历序列的结束位置
* @return 返回根结点
* */
fun recover(preOrder: IntArray, pStart: Int, pEnd: Int,
inOrder: IntArray, iStart: Int, iEnd: Int): BinaryTreeNode? {
// 判断临界位置,如果数组的起始位置超过结束位置,那么就直接返回了
if (pStart > pEnd) return null
// 根结点
val root = preOrder[pStart]
var inRootIndex = iStart
while (inRootIndex <= iEnd && inOrder[inRootIndex] != root) ++inRootIndex
// 如果查找到的index大于iEnd,那么就表明两个序列不是同一个二叉树的,抛出异常
if (inRootIndex > iEnd) throw Exception("Error input.")
// 创建当前的根结点,并且为结点赋值
val node = BinaryTreeNode(root, null, null)
// 递归重建左子树,注意:这里假设不会存在相同的value的结点,可以采用key(hashValue)-value解决。
node.left = recover(preOrder, pStart + 1, pStart + inRootIndex - iStart,
inOrder, iStart, inRootIndex - 1)
// 递归重建右子树,注意:这里假设不会存在相同的value的结点,可以采用key(hashValue)-value解决。
node.right = recover(preOrder, pStart + inRootIndex - iStart + 1, pEnd,
inOrder, inRootIndex + 1, iEnd)
// 返回创建的根结点
return node
}
return recover(preOrder, 0, preOrder.size - 1, inOrder, 0, inOrder.size - 1)
}
/**
* 二叉树的结点结构
*
* @param value 结点的值
* @param left 左结点
* @param right 右结点
* */
class BinaryTreeNode(var value: Int,
var left: BinaryTreeNode?,
var right: BinaryTreeNode?)
/**
* 前序遍历
*
* @param root 根结点
* */
fun preOrder(root: BinaryTreeNode?) {
if (root == null) {
println("根结点为空~")
return
}
val sb = StringBuilder()
fun order(root: BinaryTreeNode) {
sb.append(root.value).append(" ")
if (root.left != null) order(root.left!!)
if (root.right != null) order(root.right!!)
}
order(root)
println("前序遍历的结果为:$sb")
}
/**
* 中序遍历
*
* @param root 根结点
* */
fun inOrder(root: BinaryTreeNode?) {
if (root == null) {
println("根结点为空~")
return
}
val sb = StringBuilder()
fun order(root: BinaryTreeNode) {
if (root.left != null) order(root.left!!)
sb.append(root.value).append(" ")
if (root.right != null) order(root.right!!)
}
order(root)
println("中序遍历的结果为:$sb")
}
/**
* 后序遍历
*
* @param root 根结点
* */
fun postOrder(root: BinaryTreeNode?) {
if (root == null) {
println("根结点为空~")
return
}
val sb = StringBuilder()
fun order(root: BinaryTreeNode) {
if (root.left != null) order(root.left!!)
if (root.right != null) order(root.right!!)
sb.append(root.value).append(" ")
}
order(root)
println("后序遍历的结果为:$sb")
}
输出结果
前序遍历的结果为:1 2 4 7 3 5 6 8
中序遍历的结果为:4 7 2 1 5 3 8 6
后序遍历的结果为:7 4 2 5 8 6 3 1
和我们初始输入的内容一致。
Til next time,
LinkWorld
at 13:21