树的遍历,尤其是二叉树的遍历算法是常见并且非常重要的,关于这个问题的讨论网上已经有很多,并且很多教材也做了分析,从递归实现,到非递归用栈来实现,以及非递 归不用栈来实现,最后一种被认为是繁琐的,特别具体实现时要考虑不少细节,下面是 我整理的比较容易写出来且容易记忆的一种写法,算是抛砖引玉吧。
不能使用栈的话,可以通过向节点中添加指向父亲的指针域,用来达到回溯的效果,并且 用一个变量来记录当前节点的访问状态,分为如下三种:
左子树未遍历;
左子树已遍历(包括左子树为空);
右子树已遍历(右子树为空)。
在下面的伪代码中我是这样考虑的:
一个节点有一个域指出来它是否有左子树,一是有左子树,二是没有左子树,对这两种情 况分别处理上面提到的三种访问状态,直到迭代结束,这样我们就保证算法是正确的。而 且是容易记忆的,实际上你将会发现它们是很容易写出来的,只要你心中牢记上面考虑的 解决思路。
因为很多书上都把中序遍历作为习题,甚至有些面试题上也是这样,所以我只注释了中序, 实际上这三种方法只有访问节点的地方不一样,其他是一摸一样的。伪码的有些地方使用 了《算法导论》中的风格,如left[root]指节点root的左孩子等,大体上应该很容易阅读 的。
enum TRAVESAL_STATE
{
LEFT_NOT_TRAVERS ; //左子树未遍历
LEFT_TRAVERSED ; //左子树已遍历(包括左子树为空)
RIGHT_TRAVERSED ; //右子树已遍历(右子树为空)
}
{
LEFT_NOT_TRAVERS ; //左子树未遍历
LEFT_TRAVERSED ; //左子树已遍历(包括左子树为空)
RIGHT_TRAVERSED ; //右子树已遍历(右子树为空)
}
in-order(中序)
/******************* in-order start *******************************/
if (root == NIL )
结束 ;
//找到遍历的结束点end.
while (right [root ] ! = NIL )
{
end = right [root ] ;
}
// 记录初始状态
state = LEFT_NOT_TRAVERS ;
//这里有一个比较隐蔽的地方需要注意,不能只靠 (root != end) 判断是否
//遍历结束,如果end有左子树的话它和它的子树就遍历不了了。我们用一个变
//量来记录是否已经遍历了end,如果遍历了end那么它的子树肯定已经遍历
//了,这是中序遍历的性质。当然可以写成不需要设置变量的形式,但代码看
//起来不一定舒服。对于前序后序也有这个问题.
real_end = false ;
while ( !real_end )
{
//左子树不为空的情况
if (left [root ] ! = NIL )
{
switch (state )
{
//左子树没有遍历的话去处理左子树
case LEFT_NOT_TRAVERS :
root = left [root ] ;
state = LEFT_NOT_TRAVERS ;
break ;
//左子树已经处理完了,则访问该节点,如果没有右子树,则设置访问状态为
//右子树已遍历,见上面概述,否则去处理右子树。
case LEFT_TRAVERSED :
/****************/
访问root;
if ( root = end )
{
real_end = true ;
break ;
}
/****************/
if (right [root ] == NIL )
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
//右子树处理完了的话,向上回溯
case RIGHT_TRAVERSED :
if ( right [parent [root ] ] ! = root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
else
{
switch (state )
{
//设置访问状态
case LEFT_NOT_TRAVERS ;
state = LEFT_TRAVERSED ;
break ;
//左子树已经处理完了,则访问该节点,如果没有右子树,则设置访问状态为
//右子树已遍历,见上面概述,否则去处理右子树。
case LEFT_TRAVERSED ;
/**********************/
访问root ;
if ( root == end )
{
real_end = true ;
break ;
}
/**********************/
if(right [root ] == NIL)
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
//右子树处理完了的话,向上回溯,这是关键的地方。另外一个关键的地方是访问
//节点之后要判断
case RIGHT_TRAVERSED ;
//处理是从右子树回溯上来的情况,如果是从右子树回溯上来的,下一个将要处理的
//节点,即当前节点的父节点的状态是RIGHT_TRAVERSED,也就是说它的右子树已经
//处理过了,否则是LEFT_TRAVERSED,也就是说它的右子树还没有处理。
if ( right [parent [root ] ] ! = root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
}
/******************* in-order end **********************************/
if (root == NIL )
结束 ;
//找到遍历的结束点end.
while (right [root ] ! = NIL )
{
end = right [root ] ;
}
// 记录初始状态
state = LEFT_NOT_TRAVERS ;
//这里有一个比较隐蔽的地方需要注意,不能只靠 (root != end) 判断是否
//遍历结束,如果end有左子树的话它和它的子树就遍历不了了。我们用一个变
//量来记录是否已经遍历了end,如果遍历了end那么它的子树肯定已经遍历
//了,这是中序遍历的性质。当然可以写成不需要设置变量的形式,但代码看
//起来不一定舒服。对于前序后序也有这个问题.
real_end = false ;
while ( !real_end )
{
//左子树不为空的情况
if (left [root ] ! = NIL )
{
switch (state )
{
//左子树没有遍历的话去处理左子树
case LEFT_NOT_TRAVERS :
root = left [root ] ;
state = LEFT_NOT_TRAVERS ;
break ;
//左子树已经处理完了,则访问该节点,如果没有右子树,则设置访问状态为
//右子树已遍历,见上面概述,否则去处理右子树。
case LEFT_TRAVERSED :
/****************/
访问root;
if ( root = end )
{
real_end = true ;
break ;
}
/****************/
if (right [root ] == NIL )
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
//右子树处理完了的话,向上回溯
case RIGHT_TRAVERSED :
if ( right [parent [root ] ] ! = root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
else
{
switch (state )
{
//设置访问状态
case LEFT_NOT_TRAVERS ;
state = LEFT_TRAVERSED ;
break ;
//左子树已经处理完了,则访问该节点,如果没有右子树,则设置访问状态为
//右子树已遍历,见上面概述,否则去处理右子树。
case LEFT_TRAVERSED ;
/**********************/
访问root ;
if ( root == end )
{
real_end = true ;
break ;
}
/**********************/
if(right [root ] == NIL)
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
//右子树处理完了的话,向上回溯,这是关键的地方。另外一个关键的地方是访问
//节点之后要判断
case RIGHT_TRAVERSED ;
//处理是从右子树回溯上来的情况,如果是从右子树回溯上来的,下一个将要处理的
//节点,即当前节点的父节点的状态是RIGHT_TRAVERSED,也就是说它的右子树已经
//处理过了,否则是LEFT_TRAVERSED,也就是说它的右子树还没有处理。
if ( right [parent [root ] ] ! = root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
}
/******************* in-order end **********************************/
pre-order(前序)
/******************* pre-order start ******************************/
if (root == NIL )
结束 ;
//找到遍历的结束点end.
while ( true )
{
if ( right [root ] ! = NIL )
root = right [root ] ;
else if (left [root ] ! = NIL )
root = left [root ] ;
else
break ;
}
state = LEFT_NOT_TRAVERS ;
real_end = false ;
while ( !real_end )
{
if (left [root ] ! = NIL )
{
switch (state )
{
case LEFT_NOT_TRAVERS :
/****************/
访问root ;
if ( root = end )
{
real_end = true ;
break ;
}
/****************/
root = left [root ] ;
state = LEFT_NOT_TRAVERS ;
break ;
case LEFT_TRAVERSED :
if (right [root ] == NIL )
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
case RIGHT_TRAVERSED :
if ( right [parent [root ] ] ! = root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
else
{
switch (state )
{
case LEFT_NOT_TRAVERS ;
/****************/
访问root ;
if ( root == end )
{
real_end = true ;
break ;
}
/****************/
state = LEFT_TRAVERSED ;
break ;
case LEFT_TRAVERSED ;
if(right [root ] == NIL)
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
case RIGHT_TRAVERSED ;
if ( right [parent [root ] ] == root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
}
/******************* pre-order end ********************************/
if (root == NIL )
结束 ;
//找到遍历的结束点end.
while ( true )
{
if ( right [root ] ! = NIL )
root = right [root ] ;
else if (left [root ] ! = NIL )
root = left [root ] ;
else
break ;
}
state = LEFT_NOT_TRAVERS ;
real_end = false ;
while ( !real_end )
{
if (left [root ] ! = NIL )
{
switch (state )
{
case LEFT_NOT_TRAVERS :
/****************/
访问root ;
if ( root = end )
{
real_end = true ;
break ;
}
/****************/
root = left [root ] ;
state = LEFT_NOT_TRAVERS ;
break ;
case LEFT_TRAVERSED :
if (right [root ] == NIL )
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
case RIGHT_TRAVERSED :
if ( right [parent [root ] ] ! = root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
else
{
switch (state )
{
case LEFT_NOT_TRAVERS ;
/****************/
访问root ;
if ( root == end )
{
real_end = true ;
break ;
}
/****************/
state = LEFT_TRAVERSED ;
break ;
case LEFT_TRAVERSED ;
if(right [root ] == NIL)
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
case RIGHT_TRAVERSED ;
if ( right [parent [root ] ] == root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
}
/******************* pre-order end ********************************/
post-order(后序)
/******************* post-order start ******************************/
if (root == NIL )
结束 ;
//找到遍历的结束点end.
end = root ;
// 记录初始状态
state = LEFT_NOT_TRAVERS ;
real_end = false ;
while ( !real_end )
{
if (left [root ] ! = NIL )
{
switch (state )
{
case LEFT_NOT_TRAVERS :
root = left [root ] ;
state = LEFT_NOT_TRAVERS ;
break ;
case LEFT_TRAVERSED :
if (right [root ] == NIL )
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
case RIGHT_TRAVERSED :
/****************/
访问root ;
if ( root = end )
{
real_end = true ;
break ;
}
/****************/
if ( right [parent [root ] ] ! = root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
else
{
switch (state )
{
case LEFT_NOT_TRAVERS ;
state = LEFT_TRAVERSED ;
break ;
case LEFT_TRAVERSED ;
if(right [root ] == NIL)
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
case RIGHT_TRAVERSED ;
/****************/
访问root ;
if ( root == end )
{
real_end = true ;
break ;
}
/****************/
if ( right [parent [root ] ] ! = root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
}
/******************* post-order end ********************************/
if (root == NIL )
结束 ;
//找到遍历的结束点end.
end = root ;
// 记录初始状态
state = LEFT_NOT_TRAVERS ;
real_end = false ;
while ( !real_end )
{
if (left [root ] ! = NIL )
{
switch (state )
{
case LEFT_NOT_TRAVERS :
root = left [root ] ;
state = LEFT_NOT_TRAVERS ;
break ;
case LEFT_TRAVERSED :
if (right [root ] == NIL )
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
case RIGHT_TRAVERSED :
/****************/
访问root ;
if ( root = end )
{
real_end = true ;
break ;
}
/****************/
if ( right [parent [root ] ] ! = root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
else
{
switch (state )
{
case LEFT_NOT_TRAVERS ;
state = LEFT_TRAVERSED ;
break ;
case LEFT_TRAVERSED ;
if(right [root ] == NIL)
{
state = RIGHT_TRAVERSED ;
}
else
{
root = right [root ] ;
state = LEFT_NOT_TRAVERS ;
}
break ;
case RIGHT_TRAVERSED ;
/****************/
访问root ;
if ( root == end )
{
real_end = true ;
break ;
}
/****************/
if ( right [parent [root ] ] ! = root )
state = LEFT_TRAVERSED ;
root = parent [root ] ;
break ;
}
}
}
/******************* post-order end ********************************/