前面我们了解了二叉树的生成与遍历,并能得到正确的遍历结果。
此时我们不禁要想:能不能根据遍历结果(深度优先遍历)还原二叉树呢?
答案当然是肯定的,下面我们就探究一下。
方向分析
首先,能不能通过一个遍历结果确定唯一的二叉树?
答案是否定的,比如下图:
单单只通过前序、后序是无法确定一颗二叉树的,他们能确定节点的父子关系,但是缺失了节点间的兄弟关系;而中序遍历结果在确定根节点的情况下可以确定节点间的兄弟关系,在缺少根节点信息的情况下,无法确定父子关系。
既然通过一个遍历结果无法确定一棵二叉树,那么我们可不可以两两结合来判断?答案是可以的,因为“先序”、“后序”能确定二叉树的根节点和节点的父子关系,“中序”在根节点确定的情况下能确定节点的兄弟关系,所以“先序+中序”和“后序+中序”的组合能确定一颗二叉树,“先序+后序”的组合因为缺少节点兄弟关系故无法确定一棵树。
“先序+中序”分析及实现
如下图一颗二叉树
它的先序结果是:ABDEFGCHIJK,中序结果是:DBFEGAHCKJI,那么我们通过这两个结果的结合来还原这颗二叉树:
原理其实就是根据先序结果一层一层的确定根节点,然后根据中序结果判断左树和右树。下面我们用代码来实现一遍。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>二叉树还原</title>
</head>
<body>
<script>
const DLR = ['A', 'B', 'D', 'E', 'F', 'G', 'C', 'H', 'I', 'J', 'K']
const LDR = ['D', 'B', 'F', 'E', 'G', 'A', 'H', 'C', 'K', 'J', 'I']
function restore(dlr = [], ldr = []) {
let node
const nodeValue = dlr.shift()
const index = ldr.indexOf(nodeValue)
if (index !== -1) {
node = {}
node.value = nodeValue;
const ldr_left = ldr.slice(0, index);
const dlr_left = dlr.filter(i => ldr_left.indexOf(i) !== -1)
node.left = restore(dlr_left, ldr_left)
const ldr_right = ldr.slice(index + 1);
const dlr_right = dlr.filter(i => ldr_right.indexOf(i) !== -1)
node.right = restore(dlr_right, ldr_right)
}
return node
}
console.log(restore(DLR, LDR))
</script>
</body>
</html>
"后序+中序"分析及实现
其实和“前序+中序”一样,只不过根节点的位置在最后而不是一开始了,这里就不做另外分析说明了。
“前序+后序”分析
前面已经说明了,这两个遍历结果没有兄弟节点的关系信息,所以无法还原二叉树。