中序遍历
概念
「中序遍历」指先遍历节点的左子树,再访问节点,最后遍历节点的右子树,按照这种规则不重复地访问树中所有节点的过程。
思路
图中树的结构如下,以变量root
保存
// 节点的数据结构
function Node(value) {
this.value = value
this.left = null
this.right = null
}
root {
value: 'A',
left: {
value: 'B',
left: {
value: 'D',
left: {
value: 'H',
left: null,
right: {
value: 'K',
left: null,
right: null
}
},
right: null
},
right: {
value: 'E',
left: null,
right: null
}
},
right: {
value: 'C',
left: {
value: 'F',
left: {
value: 'I',
left: null,
right: null
},
right: null
},
right: {
value: 'G',
left: null,
right: {
value: 'J',
left: null,
right: null
}
}
}
}
设计函数,传入的参数为树的根root
,从根节点出发,遍历所有节点
/**
* 中序遍历二叉树
* 传入的参数是二叉树的根
* @param {Node} root
*/
function inOrderTraverse(root) {
let current = root // 从根节点出发
while (current) { // 找到下个节点,若不存在,则跳出循环,遍历结束
let nextNode
// 寻找下个节点
current = nextNode // 前往下个节点
}
}
先遍历节点的左子树,再访问节点,最后遍历节点的右子树。故当来到节点,发现其存在左子树时,先遍历其左子树,前往的下个节点就是左子树的根,即当前节点的左孩子,同时将当前节点保存,当其左子树遍历结束后,再访问该节点,并遍历该节点的右子树。用栈保存经过的待访问的节点,栈顶节点表示正在遍历节点的左子树。
function inOrderTraverse(root) {
let current = root, // 从根节点出发
stack = [] // 保存待访问的节点
while (current) { // 找到下个节点,若不存在,则跳出循环,遍历结束
let nextNode
if (current.left) { // 当前节点存在左子树
stack.push(current) // 保存当前节点,当其左子树遍历结束后,再访问该节点
nextNode = current.left // 前往当前节点的左孩子,遍历当前节点的左子树
}
// 未完待续
current = nextNode // 前往下个节点
}
}
当节点仅存在右子树时,因为其无左子树,所以直接访问节点,并前往节点的右孩子,开始遍历其右子树。
function inOrderTraverse(root) {
let current = root, // 从根节点出发
stack = [] // 保存待访问的节点
while (current) { // 找到下个节点,若不存在,则跳出循环,遍历结束
let nextNode
if (current.left) { // 当前节点存在左子树
stack.push(current) // 保存当前节点,当其左子树遍历结束后,再访问该节点
nextNode = current.left // 前往当前节点的左孩子,遍历当前节点的左子树
} else if (!current.left && current.right) { // 仅存在右子树
console.log(current.value) // 无左子树,直接访问节点
nextNode = current.right // 开始遍历其右子树
}
// 未完待续
current = nextNode // 前往下个节点
}
}
当节点无子树遍历,直接访问节点。同时,说明栈顶节点的左子树遍历结束。栈顶节点出栈,访问节点,开始遍历其右子树。若节点无右子树,说明栈中新的栈顶节点的左子树遍历结束。栈顶节点出栈,访问节点,开始遍历其右子树。如此循环直至节点含右子树。
function inOrderTraverse(root) {
let current = root, // 从根节点出发
stack = [] // 保存待访问的节点
while (current) { // 找到下个节点,若不存在,则跳出循环,遍历结束
let nextNode
if (current.left) { // 当前节点存在左子树
stack.push(current) // 保存当前节点,当其左子树遍历结束后,再访问该节点
nextNode = current.left // 前往当前节点的左孩子,遍历当前节点的左子树
} else if (!current.left && current.right) { // 仅存在右子树
console.log(current.value) // 无左子树,直接访问节点
nextNode = current.right // 开始遍历其右子树
} else { // 节点无子树
console.log(current.value) // 访问节点
let pop = stack.pop() // 栈顶节点的左子树遍历结束,栈顶节点出栈
while (pop && !pop.right) { // 若出栈节点不含右子树
console.log(pop.value) // 访问出栈的节点
// 此时栈中新的栈顶节点的左子树遍历结束
pop = stack.pop() // 栈顶节点出栈
} // 直到栈空或弹出含右子树的节点
if (pop) { // 含右子树的节点
console.log(pop.value) // 访问节点
nextNode = pop.right // 前往其右孩子,开始遍历其右子树
} else { // 栈空
nextNode = null // 找不到下个节点,循环结束
}
}
current = nextNode // 前往下个节点
}
}
代码
整理后代码如下
/**
* 中序遍历二叉树
* 传入的参数是二叉树的根
* @param {Node} root
*/
const inOrderTraverse = root => {
let current = root,
stack = []
while (current) {
if (current.left) {
stack.push(current)
current = current.left
} else if (!current.left && current.right) {
console.log(current.value)
current = current.right
} else {
console.log(current.value)
let pop = stack.pop()
while (pop && !pop.right) {
console.log(pop.value)
pop = stack.pop()
}
pop && console.log(pop.value)
current = pop ? pop.right : null
}
}
}
inOrderTraverse(binaryTree.root)
// H K D B E A I F C G J
其实没那么复杂,别人家代码
const inOrderTraverse = root => {
let current = root,
stack = []
while (current || stack.length !== 0) {
if (current) {
stack.push(current)
current = current.left // 遍历左子树
} else {
current = stack.pop()
current && console.log(current.value) // 访问节点
current = current ? current.right : null // 遍历右子树
}
}
}
inOrderTraverse(binaryTree.root)
// H K D B E A I F C G J
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。