推导前序序列
已知二叉树的中序序列是ABCDEFG,后序序列是BDCAFGE,求前序序列。
思路
二叉树的后序序列是按照「左子树」,「右子树」,「根」的顺序排列的,序列中最后一个元素代表该二叉树的根节点。
二叉树的前序序列是按照「根」,「左子树」,「右子树」的顺序排列的,序列中第一个元素代表该二叉树的根节点。故通过已知的后序序列可以发现根,作为前序序列的第一个元素。
二叉树的中序序列是按照「左子树」,「根」,「右子树」的顺序排列的,根将左子树的序列和右子树的序列分割在左右两边。通过后序序列发现根,找到其在中序序列的位置,从而在中序序列中发现左子树和右子树并获得它们的长度,最后在后序序列中找到对应的左子树和右子树的后序序列。
这样求某二叉树的前序序列,变成了求该二叉树左子树的前序序列和该二叉树右子树的前序序列。
递归解法
一棵树,先找到其根,将其压入栈中,再将其分割成左右子树,按左子树,右子树的顺序,找子树的根,将其压入栈中,直到子树不可分割,得到整棵树的前序序列。
/**
* 返回二叉树的前序序列
* @param {array} inOrder
* @param {array} postOrder
* @returns {array} preOrder
*/
function findPreOrder (inOrder, postOrder) {
let preOrder = []
// 保存前序序列
!function findRoot(inOrder, postOrder) {
if (inOrder.length <= 1) {
// 若序列的长度为0或1,停止递归调用
inOrder[0] && preOrder.push(inOrder[0])
// 若序列长度为1,则它就是该树的根节点,根入栈
// 在先序序列中,树的根排列顺序先于子树的根
return
} else {
const root = postOrder[postOrder.length - 1]
// 找到根节点
preOrder.push(root)
// 根入栈
const index = inOrder.indexOf(root)
// 找到根节点在中序序列中的位置
const newInOrderLeft = inOrder.slice(0, index)
// 根据根的位置找出左子树的中序序列
const newInOrderRight = inOrder.slice(index + 1)
// 根据根的位置找出右子树的中序序列
const newPostOrderLeft = postOrder.slice(0, newInOrderLeft.length)
// 根据左子树中序序列的长度找出其后序序列
const newPostOrderRight = postOrder.slice(newInOrderLeft.length, newInOrderRight.length + newInOrderLeft.length)
// 根据右子树中序序列的长度找出其后序序列
findRoot(newInOrderLeft, newPostOrderLeft)
// 找到左子树的根
findRoot(newInOrderRight, newPostOrderRight)
// 找到右子树的根
// 在先序序列中,左子树的根排列顺序先于右子树的根
}
}(inOrder, postOrder)
return preOrder
// 返回前序序列
}
let preOrder = findPreOrder(Array.from('ABCDEFG'), Array.from('BDCAFGE'))
console.log(preOrder)
// [ 'E', 'A', 'C', 'B', 'D', 'G', 'F' ]
非递归解法
非递归解法和递归解法的思路相同,找到根,压入栈,分割左右子树,找到它们的根,压入栈。
不同之处在于用inOrderArr保存待分割的树的中序序列,用postOrderArr保存待分割的树的后序序列。
const findPreOrder = (inOrder, postOrder) => {
let preOrder = [],
// 保存前序序列
inOrderArr = [inOrder],
// 存储待分割的树的中序序列,初始状态为传入的树的中序序列
postOrderArr = [postOrder]
// 存储待分割的树的后序序列,初始状态为传入的树的后序序列
while (preOrder.length < inOrder.length) {
let inOrderEle = inOrderArr.shift(),
// 取出子树的中序序列
postOrderEle = postOrderArr.shift()
// 取出子树的后序序列
if (inOrderEle.length <= 1) {
// 若取出的序列的长度为0或1,就不再分割
inOrderEle[0] && preOrder.push(inOrderEle[0])
// 若取出的序列的长度为1,则为根,入栈
} else {
let root = postOrderEle[postOrderEle.length - 1]
// 找到根
preOrder.push(root)
// 根入栈
// 开始分割左右子树
const index = inOrderEle.indexOf(root)
const newinOrderEle1 = inOrderEle.slice(0, index)
const newinOrderEle2 = inOrderEle.slice(index + 1)
inOrderArr = [newinOrderEle1, newinOrderEle2, ...inOrderArr] // 用分割后的左右子树代替原先的树
const newpostOrderEle1 = postOrderEle.slice(0, newinOrderEle1.length)
const newpostOrderEle2 = postOrderEle.slice(newinOrderEle1.length, newinOrderEle1.length + newinOrderEle2.length)
postOrderArr = [newpostOrderEle1, newpostOrderEle2, ...postOrderArr] // 用分割后的左右子树代替原先的树
}
}
return preOrder
// 返回前序序列
}
let preOrder = findPreOrder(Array.from('ABCDEFG'), Array.from('BDCAFGE'))
console.log(preOrder)
// [ 'E', 'A', 'C', 'B', 'D', 'G', 'F' ]
推导后序序列
已知二叉树的前序序列是EACBDGF,中序序列是ABCDEFG,求后序序列。
思路
通过前序序列找到根,找到根在中序序列的位置,将树分割成左右子树,按照先右子树再左子树的顺序找到子树的根,直至子树不可分割。先找到的根排在后找到的根的后面。
和推导前序序列,相比思路大致相同,实现细节上,找树的根的方式不同,分割子树的方式不同,保存根的方式不同。
递归解法
const findPostOrder = (preOrder, inOrder) => {
let postOrder = []
// 保存后序序列
!function findRoot(preOrder, inOrder) {
if(inOrder.length <= 1){
// 若序列的长度为0或1,停止递归调用
inOrder[0] && postOrder.unshift(inOrder[0])
// 若序列长度为1,则它就是该树的根节点,根入栈
// 在后序序列中,树的根排列顺序后于子树的根
return
}else{
const root = preOrder[0]
// 找到根节点
postOrder.unshift(root)
// 保存根
const index = inOrder.indexOf(root)
// 找到根节点在中序序列中的位置
const newInOrderLeft = inOrder.slice(0, index)
// 根据根的位置找出左子树的中序序列
const newInOrderRight = inOrder.slice(index + 1)
// 根据根的位置找出右子树的中序序列
const newPreOrderLeft = preOrder.slice(1, newInOrderLeft.length + 1)
// 根据左子树中序序列的长度找出其前序序列
const newPreOrderRight = preOrder.slice(newInOrderLeft.length + 1)
// 根据右子树中序序列的长度找出其前序序列
findRoot(newPreOrderRight, newInOrderRight)
// 找到右子树的根
findRoot(newPreOrderLeft, newInOrderLeft)
// 找到左子树的根
}
}(preOrder, inOrder)
return postOrder
// 返回后序序列
}
let postOrder = findPostOrder(Array.from('EACBDGF'), Array.from('ABCDEFG'))
console.log(postOrder)
// [ 'B', 'D', 'C', 'A', 'F', 'G', 'E' ]
非递归解法
非递归解法和递归解法的思路相同,找到根并保存,分割左右子树,找到它们的根并保存。
不同之处在于用preOrderArr保存待分割的树的前序序列,用inOrderArr保存待分割的树的中序序列,且每次取最后一个元素开始处理。
const findPostOrder = (preOrder, inOrder) => {
let postOrder = [],
preOrderArr = [preOrder],
inOrderArr = [inOrder]
while (postOrder.length < inOrder.length) {
let inOrderEle = inOrderArr.pop(),
preOrderEle = preOrderArr.pop()
if (inOrderEle.length <= 1) {
inOrderEle[0] && postOrder.unshift(inOrderEle[0])
} else {
let root = preOrderEle[0]
postOrder.unshift(root)
const index = inOrderEle.indexOf(root)
const newinOrderEle1 = inOrderEle.slice(0, index)
const newinOrderEle2 = inOrderEle.slice(index + 1)
inOrderArr = [...inOrderArr, newinOrderEle1, newinOrderEle2]
const newpreOrderEle1 = preOrderEle.slice(1, newinOrderEle1.length + 1)
const newpreOrderEle2 = preOrderEle.slice(newinOrderEle1.length + 1)
preOrderArr = [...preOrderArr, newpreOrderEle1, newpreOrderEle2]
}
}
return postOrder
}
console.log(findPostOrder(Array.from('EACBDGF'), Array.from('ABCDEFG')))
// [ 'B', 'D', 'C', 'A', 'F', 'G', 'E' ]
推导中序序列
那是不可能的!
已知前序序列ABC,后序序列CBA,求中序序列。
这是一道多解题。
附
推荐结合nodemon在node环境下学习算法。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。