一、先序遍历
1.算法思路
非递归遍历需要用栈实现。在先序遍历中,需要先将根节点压入栈中,然后不断对栈中的节点进行循环遍历。首先进行出栈操作,获取当前出栈节点的数据,之后将当前节点右孩子节点压入栈中(若存在),然后将左孩子节点压入栈中(若存在)。之后开始下一轮循环。
注:因为栈是先进后出,所以先让右孩子进栈,这样下一轮循环时出栈的就是左孩子。(符合根左右的标准)
2.代码实现
let preorderArr = []
function preorder(node) {
let stack = []
stack.push(node)
while (stack.length !== 0) { // 不断对栈中元素进行遍历
let treeNode = stack.pop()
preorderArr.push(treeNode.data); // 提取出栈节点数据
if (treeNode.rChild) { // 后进先出,所以先压入右孩子节点
stack.push(treeNode.rChild)
}
if (treeNode.lChild) {
stack.push(treeNode.lChild)
}
}
}
console.log('先序:');
preorder(tree)
console.log(preorderArr);
运行结果:
二、中序遍历
1.算法思路
中序遍历时因为左孩子优先权最大,所以我们需要不断将当前节点压入栈中,然后去遍历其左孩子,只有当前节点的左孩子不存在时再进行退栈操作,去遍历其右孩子,直到当前栈为空且所有树节点全部被遍历。
2.代码实现
具体代码:
// 中序非递归
let inorderArr = []
function inorder(node) {
let stack = []
let treeNode = node
while (stack.length !== 0 || treeNode) { // 当前树的节点尚未遍历完,或者栈中数据不为空时,执行循环
if (treeNode) { // 不断将每个节点压栈
stack.push(treeNode)
treeNode = treeNode.lChild
} else { // 当左孩子不存在时则逐步退栈并遍历右子树
treeNode = stack.pop()
inorderArr.push(treeNode.data);
treeNode = treeNode.rChild
}
}
}
console.log('中序:');
inorder(tree)
console.log(inorderArr);
运行结果:
三、后序遍历
1.算法思路
后续遍历其实有一个技巧。我们知道后序遍历其实是‘左右根’,如果我们将遍历的结果逆序获得的就是‘根右左’的遍历顺序。而我们上面的先序遍历代码中就有说到,当右孩子先进栈、左孩子后进栈则左孩子先出栈、先被遍历。所以我们只要调整这两句代码顺序就获得了‘根右左’的遍历顺序。,再将结果逆转就得到‘左右根’的后序遍历了。
2.代码实现
// 后序非递归
let postorderArr = []
function postorder(node) {
let stack = []
stack.push(node)
while (stack.length !== 0) {
let treeNode = stack.pop()
postorderArr.push(treeNode.data);
if (treeNode.lChild) { // 与先序遍历相比,调整左、右孩子的进栈顺序
stack.push(treeNode.lChild)
}
if (treeNode.rChild) { // 后进先出,所以先压入右子树
stack.push(treeNode.rChild)
}
}
}
postorder(tree)
console.log('后序:');
console.log(postorderArr.reverse()); // 逆转最后的结果即得到后续遍历
运行结果: