二叉树遍历非递归写法之大统一

        在学习二叉树遍历时,大家都很容易接受递归写法,好理解。对于非递归写法,基本思想是用栈消除递归,但是教材上的前序、中序和后序基本上三个写法,还很难理解。博主亲身经历,找工作中,考查二叉树遍历的非递归写法还是常见的。所以决心整理出此文,方便理解和记忆二叉树遍历。

        来复习一下二叉树的递归遍历。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. struct TreeNode {  
  2.     int data;  
  3.     TreeNode * left, * right;  
  4. };  
  5.   
  6. void preOrderTraverse(TreeNode * root, void (*visit)(TreeNode *)) {  
  7.     if (root) {  
  8.         visit(root);  
  9.         preOrderTraverse(root->left, visit);  
  10.         preOrderTraverse(root->right, visit);  
  11.     }  
  12. }  
  13.   
  14. void inOrderTraverse(TreeNode * root, void (*visit)(TreeNode *)) {  
  15.     if (root) {          
  16.         inOrderTraverse(root->left, visit);  
  17.         visit(root);  
  18.         inOrderTraverse(root->right, visit);  
  19.     }  
  20. }  
  21.   
  22. void postOrderTraverse(TreeNode * root, void (*visit)(TreeNode *)) {  
  23.     if (root) {  
  24.         postOrderTraverse(root->left, visit);  
  25.         postOrderTraverse(root->right, visit);  
  26.         visit(root);          
  27.     }  
  28. }  

         首先,博主找到了一篇类似论文,《更简单的非递归遍历二叉树的方法》,该文非常漂亮的将三种遍历方法统一起来,除了有三行代码的顺序不一样外。该实现中,对每个节点定义了一个pair对,通过改变局部入栈顺序实现整体遍历的不同。

        本文意图使用常见的二叉树遍历的形式,简洁的实现这三种遍历。

void preOrderTraverseNonrecursive(TreeNode * root, void (*visit)(TreeNode *)) {
    stack<TreeNode *> s;
    TreeNode * p = root;
    while(p != NULL || !s.empty()) {
        while(p != NULL) {
           
visit(p);
            s.push(p);
            p = p->left;
        }
        if (!s.empty()) {
            p = s.top();
            p = p->right;
            s.pop();
        }
    } 
}

void inOrderTraverseNonrecursive(TreeNode * root, void (*visit)(TreeNode *)) {
    stack<TreeNode *> s;
    TreeNode * p = root;
    while(p != NULL || !s.empty()) {
        while(p != NULL) {            
            s.push(p);
            p = p->left;
        }
        if (!s.empty()) {
            p = s.top();
            visit(p);
            p = p->right;
            s.pop();
        }
    } 
}

void postOrderTraverseNonrecursive(TreeNode * root, void (*visit)(TreeNode *)) {
    stack<TreeNode *> s;
    TreeNode * p = root, *pre = NULL;
    while(p != NULL || !s.empty()) {
        while(p != NULL) {            
            s.push(p);
            p = p->left;
        }
        if (!s.empty()) {
            p = s.top();
            if( p->right == NULL || p->right == pre) {
                visit(p);

                pre = p;

                p = NULL;

                s.pop();
            }
            else {
                p = p->right;
            }
        }
    } 
}

        在前序遍历的代码中,最外层是while循环直到栈空且p为空,里面的while循环,不停访问根节点p,且迭代p=p->right,直到p为空。此时,从root开始的所有左分支的根节点均已被访问。到if分支时,取栈顶节点,切换到其右子树开始迭代。

        前序遍历和中序遍历区别仅在于visit函数的调用位置不同中序和后序的区别在于调用visit的时机不一致。教材上的实现中,后序遍历需要一个标志来记录当前节点是否被访问过。在本文的实现中,通过一个pre指针记录被访问的最后一个元素,通过p->right==pre的比较来确认p是否被访问过。当然,直接访问p的另一情形是p没有右孩子。在这样的实现下,中序和后序的区别尽可能小。三种实现中,代码结构较一致,只有较少的变动。

        希望本文可以方便那些学习非递归遍历二叉树的同学。

        欢迎大家留言讨论!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值