二叉树的三种遍历递归代码很容易实现。自己写下非递归算法有助于加深理解二叉树数据结构,代码是思路的实现,在纸上画个二叉树,手工实践下后序遍历的过程。如果会写先根序和中根序的非递归代码,稍微改一下,就容易得到后序非递归代码了。
不论哪种遍历,有个共同点,就是左边始终先于右边访问。大体思路和先/中根序一样,利用一个辅助栈,暂存一些后访问节点。只不过后序相对麻烦点,不像先/中根序遍历,后两者根节点先于根的右子树访问,根节点访问之后就可以“扔掉”,后序遍历需要一直保存根节点,直到其右子树访问完,最后再访问根节点。
后序需要标记每个节点是否被访问过,可以有两种方法:可以在二叉树节点结构增加一个标志域,标记当前节点是否已经被访问,见版本1; 也可不用在每个节点增加标志域,而是用set存储每个访问过的节点,见版本2。
具体代码如下:
手工画个例子,照着代码走一遍,加深理解。
/* 二叉树节点结构 */
struct BinaryTreeNode{
int data;
BinaryTreeNode* lchild;
BinaryTreeNode* rchild;
bool hasBeenAccessed; /* 其实不用这个域也可以,见下面第二个版本 */
BinaryTreeNode(int _data = -1):
data(_data),lchild(NULL),rchild(NULL),hasBeenAccessed(false){}
};
/* 版本一: 每个节点保存访问标志 */
void postTraverseNonRecursively( BinaryTreeNode* pRoot ) {
if( pRoot == NULL ) {
printf("Empty tree!\n");
return;
}
stack stk; /* 使用C++标准容器适配器stack */
stk.push( pRoot );
BinaryTreeNode *pRightChild;
BinaryTreeNode *pCurNode = pRoot ->lchild; /* 当前处理节点 */
while( !stk.empty() ) {
/* 往左走,走到最左,走过的节点依次进栈 */
while( pCurNode != NULL && pCurNode ->hasBeenAccessed == false ) { /* 注意循环的条件 */
stk.push( pCurNode );
pCurNode = pCurNode ->lchild;
}
pCurNode = stk.top();
stk.pop();
pRightChild = pCurNode ->rchild;
if( pRightChild == NULL || pRightChild ->hasBeenAccessed == true ) {
printf( "%d ", pCurNode ->data ); /* 访问该节点 */
pCurNode ->hasBeenAccessed = true; /* 设置访问标记 */
} else /* if( pRightChild != NULL && pRightChild ->hasBeenAccessed == false ) */
{
stk.push( pCurNode );
stk.push( pRightChild );
pCurNode = pRightChild ->lchild; /* 重新设置当前处理节点为右子树的左孩子 */
}
}
}
/* 版本二: 节点不用访问标志域, 而是用set存储已经访问过的节点。大部分代码不变。 */
void postTraverseNonRecursively2( BinaryTreeNode* pRoot ) {
set visited; /* 存放已经访问过的节点 */
if( pRoot == NULL ) {
printf("Empty tree!\n");
return;
}
stack stk; /* 使用C++标准容器适配器stack */
stk.push( pRoot );
BinaryTreeNode *pRightChild;
BinaryTreeNode *pCurNode = pRoot ->lchild; /* 当前处理节点 */
while( !stk.empty() ) {
/* 往左走,走到最左,走过的节点依次进栈 */
while( pCurNode != NULL && visited.count(pCurNode) == 0 ) { /* 注意循环的条件 */
stk.push( pCurNode );
pCurNode = pCurNode ->lchild;
}
pCurNode = stk.top();
stk.pop();
pRightChild = pCurNode ->rchild;
if( pRightChild == NULL || visited.count(pRightChild) == 1 ) {
printf( "%d ", pCurNode ->data ); /* 访问该节点 */
visited.insert(pCurNode); /* 设置访问标记 */
} else /* if( pRightChild != NULL && visited.count(pRightChild) == 0 ) */
{
stk.push( pCurNode );
stk.push( pRightChild );
pCurNode = pRightChild ->lchild; /* 重新设置当前处理节点为右子树的左孩子 */
}
}
}