今天看到这样两篇证明二叉树非递归算法循环不变性的文章,如此的碉堡,好吧看到计算机工程的文章和我江西师大学报的文章都如此,我整个人都不好了~~~
http://wenku.baidu.com/view/582a0e7427284b73f2425009.html
http://jaq1.d139.6266668.cn/UploadFiles/attachment/20115178861129.pdf
事实说明,这种算法一般人使创造不出来的,所以只要去大致理解加记忆就可以了~~~和用数学定理是一样的~~~
一直想要写的 二叉树 中序 先序 后序遍历算法
当年学习DS最虚的就是这个,因为非递归算法复杂,测试数据不好弄,只能一个一个手动插入。感觉明显比图的难,虽然大家都觉得图更难。。。。。递归的太简单了,就不写了。关键是非递归版本。
先序:
我自己的版本:
void RootPreTraverse(Node* p)
{
Stack S;
while(S not empty)
{
p=S.top();
S.pop();
Show(p);
if(p->right!=null)
S.push(p->right);
if(p->left!=null)
S.push(p->left);
}
}后来发现用下面的非递归版本,leetcode提交memory exceed, 而我写的这个版本不会的,估计是在某些case情况下上面这个版本栈空间使用更少,但是还没分析出原因,后面再仔细分析下。经过纸上模拟运行分析,两个都是O(logn) 数量级的空间复杂度,所以可能是某个case,下面版本内存溢出,这个设计其实不是很合理,时间复杂度其实只是一个上届,表示不会比这个数量级更多了。两个代码都是一样,不会出现同一层的结点同时在栈里,所以空间复杂度为O(h), 即O(logn)后来发现和层序遍历有点相似,区别就在于 用了栈而不是队列,而且入的顺序换一换,否则到时出栈就错了。
感觉有点微妙,虽然比较容易写出来,但是还是有点悬乎,队列换栈差异这么大!
网上版本(感觉不好写) www.cppblog.com/ngaut/archive/2006/01/01/2351.aspx
void BT_PreOrderNoRec(pTreeT root)
{
stack<treeT *> s;
while ((NULL != root) || !s.empty())
{
if (NULL != root)
{
visit(root);
s.push(root);
root = root->left;
}
else //该版本代码相比于下面比较清sang,
{
root = s.top();
s.pop();
root = root->right;
}
}
}下面这个和上面是一个思路 blog.youkuaiyun.com/fansongy/article/details/6798278
void preorder_dev(bintree t){
seqstack s;
s.top = -1; //因为top在这里表示了数组中的位置,所以空为-1
if(!t){
printf("the tree is empty\n");
}else{
while(t || s.stop != -1){
while(t){ //只要结点不为空就应该入栈保存,与其左右结点无关
printf("%c ",t->data);
push(&s,t);
t= t->lchild;
} //令有一个版本http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html,这里加了判断if(S not empty()), 感觉有点画蛇添足。因为此处如果栈为空则说明上面while 一定没执行,t为空,同时S empty()那么上一次外层while已经退出了,遍历结束了。可见,此处栈必不空,上面url楼主加了一个永真判断
t=pop(&s);
t=t->rchild;
}
}
}中序:www.cppblog.com/ngaut/archive/2006/01/01/2351.aspx
void BT_InOrderNoRec(pTreeT root)
{
stack<treeT *> s;
while ((NULL != root) || !s.empty())
{
if (NULL != root)
{
s.push(root);
root = root->left;
}
else
{
root = s.top();
visit(root);
s.pop();
root = root->right;
}
}
}
//和上面preorder_dev 几乎如出一辙,从一个show 的地方就可以看出本质区别,赞博主
void midorder(bintree t){
seqstack s;
s.top = -1;
if(!t){
printf("the tree is empty!\n");
}else{
while(t ||s.top != -1){
while(t){
push(&s,t);
t= t->lchild;
}
t=pop(&s);
printf("%c ",t->data);
t=t->rchild;
}
}
}后序由于有点复杂,先搁置,集中力量打击主要部分。
从逻辑上看,两人其实是一样的,如果t不空,执行if,到while 因此次数if 等价于里面执行while,如果t空,则执行else,与另一个代码一致
另外某博客写的
blog.youkuaiyun.com/shunrei/article/details/5680579#reply
递归转非递归的模式其实不大general。。所以这个还得暂时没有通用方法。。。
<pre name="code" class="cpp">将初始状态s0进栈
while (栈不为空)
{
退栈,将栈顶元素赋给s;
if (s是要找的结果) 返回;
else {
寻找到s的相关状态s1;
将s1进栈
}
}后序
//用一个标志位记录是否 左右孩子是否已经被压过栈,如果压过栈了,如果没压栈,会先压站,然后再把该节点压站,因为后面
还要访问(后序),此外还需要把标志位
void PostOrder(TNode* root)
{
Stack S;
if( root != NULL )
{
S.push(root);
}
while ( !S.empty() )
{
TNode* node = S.pop();
if ( node->bPushed )
{ // 如果标识位为true,则表示其左右子树都已经入栈,那么现在就需要访问该节点了
Visit(node);
}
else
{ // 左右子树尚未入栈,则依次将 右节点,左节点,根节点 入栈
if ( node->right != NULL )
{
node->right->bPushed = false; // 左右子树均设置为false
S.push(node->right);
}
if ( node->left != NULL )
{
node->left->bPushed = false;
S.push(node->left);
}
node->bPushed = true; // 根节点标志位为true
S.push(node);
}
}
}后来看了师弟站站的博客,发现后序有一种类似于先序的栈算法,不是p指针往下走,而是靠压站顺序那种,感觉这种代码更简单,极力顶一下。里面访问栈顶,
如果孩子都已经访问过了,就可以访问栈顶了,用一个pre保持跟进,记录前一结点,如果pre是左孩子,说明右孩子空,孩子访问过了,如果pre是右孩子更说明左右孩子都访问过了,如果左孩子有的话,要么没有左右孩子就大胆的访问栈顶,还有一种情况,就是pre!=null 似乎只有第一次的时候pre==NULL,其实含义是表示pre不是初值,也即pre已经指向了后续遍历中的点了,所以当前的p的前一个点也必然是pre,否则如果不用pre!=NULL过滤的话,可能root只有左子树或者右子树的时候,一开始就要访问根了,而失去了左右根的顺序,加了pre!=NULL, 就可以去掉这种情况,如果p是根,强制其走下面压站的操作,除非左右都为空了,也即不能用pre是否是他孩子来访问他,因为pre还没有指向后续遍历中的点。分析好累啊。。。。
class Solution {
public:
vector<int> postorderTraversal(TreeNode *root) {
vector<int> postvec;
if(root==NULL) return postvec;
stack<TreeNode* > s;
s.push(root);
TreeNode* pre=NULL, *p;
while(!s.empty())
{
p=s.top();
if((p->left==NULL && p->right==NULL) || (pre!=NULL && (p->left==pre || p->right==pre)))
{
postvec.push_back(p->val);
s.pop();
pre=p;
}
else
{
if(p->right!=NULL)
s.push(p->right);
if(p->left!=NULL)
s.push(p->left);
}
}
return postvec;
}
};师弟战神的博客 http://www.chengzhanzhan.cn/leetcode-2/binary-tree-postorder-traversal/
http://www.cnblogs.com/baiyanhuang/archive/2011/02/12/1947435.html
这篇还没看完,mark

本文详细解析了二叉树的非递归先序、中序和后序遍历算法,包括代码实现和空间复杂度分析。通过对比不同版本的算法,揭示了非递归遍历的独特之处,并提供了优化思考。
467

被折叠的 条评论
为什么被折叠?



