本文主要讲述二叉树的先序、中序、后序递归遍历及非递归遍历,并讲述如何使用JavaScript实现遍历逻辑。
下面,我们通过一个例子来回顾一下先序、中序、后序遍历:
上面二叉树先序、中序、后序遍历结果分别为:
先序:ABCDEF
中序:CBDAEF
后序:CDBFEA
从上例可以总结出先序、中序、后序的规则:
先序:遍历到一个节点时,输出节点的值,然后遍历此节点的左子树,接着遍历此节点的右子树。
中序:遍历到一个节点时,先暂时存下来此节点,然后遍历此节点的左子树,然后输出节点的值,接着遍历此节点的右子树。
后序:遍历到一个节点时,先暂时存下来此节点,然后遍历此节点的左子树,接着遍历此节点的右子树,最后输出此节点的值。
JavaScript代码实现
开始讲述代码实现之前,先给出树节点构造函数:
function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
}
先序遍历
先序遍历递归实现
function prefaceTraverse(root){
if(root!==null){
console.log(root.val);
prefaceTraverse(root.left);
prefaceTraverse(root.right);
}
}
先序遍历非递归实现
function prefaceTraverse(root){
var stack = [];//定义一个暂时存储节点的栈
var node = root;//定义一个游动节点
//只要节点不为空,或者存储节点的栈不为空,就需要一直执行
while(node!==null || stack.length >0){
//只要节点不为空,根据先序遍历特点,一直向左走
while(node !== null){
console.log(node.val);//打印节点值
stack.push(node);//把已经遍历打印的节点入栈
node = node.left;//向左子树走
}
//上述循环跳出,说明,向左走走到叶节点了,若栈不为空
//出栈一个节点,然后把node赋值为这个节点的右子树
//接着就会继续从上述第一层循环开始遍历node的右子树
if(stack.length >0){
node = stack.pop();
node = node.right;
}
}
}
中序遍历
中序遍历递归实现
function inOrderTraverse(root){
if(root!==null){
inOrderTraverse(root.left);
console.log(root.val);
inOrderTraverse(root.right);
}
}
中序遍历非递归实现
function inOrderTraverse(root){
var stack = [];//定义一个暂时存储节点的栈
var node = root;//定义一个游动节点
//只要节点不为空,或者存储节点的栈不为空,就需要一直执行
while(node!==null || stack.length >0){
//只要节点不为空,根据中序遍历特点,一直向左走
while(node !== null){
stack.push(node);//把节点入栈
node = node.left;//向左子树走
}
//上述循环跳出,说明,向左走走到叶节点了,若栈不为空
//出栈一个节点,然后打印出节点值
//然后把node赋值为这个节点的右子树
//接着就会继续从上述第一层循环开始遍历node的右子树
if(stack.length >0){
node = stack.pop();
console.log(node.val);//打印节点值
node = node.right;
}
}
}
后序遍历
后序遍历递归实现
function postOrderTraverse(root){
if(root!==null){
postOrderTraverse(root.left);
postOrderTraverse(root.right);
console.log(root.val);
}
}
后序遍历非递归实现
后序遍历思想:
后序遍历和中序遍历不太一样,后序遍历判断当前节点是否可输出的条件是:当前节点的右子树是否遍历完成。
这个时候就需要我们增加一个变量lastNode去表标识上一个遍历的节点,若当前节点的右子树等于lastNode,说明就可输出当前节点。
function postOrderTraverse(root){
var stack = [];//定义一个暂时存储节点的栈
var node = root;//定义一个游动节点
var lastNode = root;//标识上次遍历输出的节点
//只要节点不为空,或者存储节点的栈不为空,就需要一直执行
while(node!==null || stack.length >0){
//只要节点不为空,根据先序遍历特点,一直向左走
while(node !== null){
stack.push(node);//把节点入栈
node = node.left;//向左子树走
}
//出栈栈顶节点,若此节点的右子树已经遍历,或者为空
//可以直接输出此节点
node = stack[stack.length-1];
if(node.right===null || node.right === lastNode){
console.log(node.val);//打印节点值
stack.pop();
lastNode = node;
node =null;
}
else{
//继续遍历右子树
node = node.right;
}
}
}