二叉树专题-lintcode非递归遍历与总结

来源:互联网 发布:知君本无邪 编辑:程序博客网 时间:2024/06/07 00:18

  紧接着上一篇文章讨论后序遍历的非递归写法,可以先看看上一篇文章:递归与非递归遍历

  上一篇文章已经讨论到了:后序遍历要求最后访问根节点,访问完左子树后,要想进入右子树,无法直接跳入左子树,必须要有根的信息,从根节点才可进入右子树。但是栈中只保存了一次根的信息,出栈后获取了根,访问右子树,访问完了后便丢失了根的信息。这个特点导致了后序遍历的非递归写法比先序,中序复杂。

  我们有了将节点入栈2次的想法。第一次出栈,只是作为一个中间量,为了通过这个量进入到右子树,第二次出栈,访问,便实现了后序的要求。当然,若一个节点没有右子树,只入栈一次,访问完左子树后,直接访问根就好了。

  很快有了个最直接的解决方法:直接在结构体中添加一个flag,用来标志是否为第一次访问,很容易按照这个思路实现。但一些在线编程平台不允许修改结构体,那么我们可以定义一个q,表示上一次访问的节点。只有跟的右子树恰好为p时,表示左右子树访问完了,就可以访问根了。否则,需要再次入栈。

  下面给出代码,并用例子说一些细节问题

void getResult(vector<int>& result,TreeNode * root){        /*递归版本        if(!root) return;        getResult(result,root->left);        getResult(result,root->right);        result.push_back(root->val);        */                //非递归版        stack<TreeNode*> s;        TreeNode* p=root;        if(p==NULL) return;        s.push(p);//入栈        p=p->left;        while(!s.empty()){            //不断向左侧走            while(p!=NULL){                s.push(p);                p=p->left;            }            //执行到这里时,表示已经走到了当前子树的最左侧,p为NULL            TreeNode* q=NULL;//表示上一次访问的节点            while(!s.empty()){                p=s.top();//取栈顶元素                s.pop();//出栈                //按照左右根的顺序访问,若p的右子树为上一次访问的q,则p的左右                //均访问了,可以访问p了                if(p->right==q){                    result.push_back(p->val);                    q=p;//更新上一次访问的节点                }                else{//说明右子树还未访问,需要将p再次入栈,同时转向右                //去访问右子树                    s.push(p);                    p=p->right;                    break;//这个break是关键                }            }        }    }



  只写了一部分执行的流程,与我们分析的是一致的,关键在于从左子树跳到右子树的那条break语句

  我们由分析可以看出来:

  要想后序遍历一棵二叉树,首先要指针不断向左侧走,并存取路径;上例中,访问完了b为根的子树,那么跳到c,要处理以c为根的子树,必然要从c开始不断向左走

  故向右跳的时候需要一条break语句,来终止不停的出栈



最后,总结一下3种非递归遍历的异同(先序和中序的代码可以参考上一篇文章)

1.程序是否执行的判断条件

先序中序后序栈非空,p非空栈非空,p非空栈非空

  原因:根左右,左根右的访问顺序,p指针最后是访问右子树,最后p会指向最右侧的那个NULL,故栈为空,p不为空是仍然要执行,两者都空才结束

  而对于后序遍历,最后访问的是最最上面的根,不需要对p的判断

2.递归遍历与非递归遍历的一致性

  递归中,左根右,根左右,左右根,无论哪种访问顺序,最先都是向左子树递归,故非递归写法中首先也是指针不断向左侧走。我们只是用栈模拟了递归中的入栈出栈。

想明白了这些,感觉非递归的写法也不是很难的,都是一一对应的,根据递归的流程直接转换过来就好。

原创粉丝点击