zoukankan      html  css  js  c++  java
  • 重现二叉树非递归算法的构建过程

    递归完毕树的遍历非常好理解,倘若是非递归。不要告诉我算法导论上有,我要maker的思考过程
    既然递归可以实现,那就模拟递归。

    递归的本质就是压栈。
    首先简单树。观察递归的压栈过程
    这里写图片描写叙述
    A、B即使节点的数据也代表节点的地址。
    对这棵树使用递归完毕前序创建

    #include <iostream>
    using namespace std;
    struct treenode;
    typedef struct treenode* TREE;
    struct treenode
    {
     char data;
     TREE left;
     TREE right;
    };
    void tree_front(TREE& T)
    {
     char buff;
     cout<<"please input ,or NULL(*)"<<endl;
     cin>>buff;
     if(buff!='*')
     {
      T=new struct treenode;
      if(T==NULL)
       exit(-1);
      T->data=buff;
      tree_front(T->left);
      tree_front(T->right);
     }
     else
      T=NULL;
    }
    int main()
    {
     TREE T;
     tree_front(T);
    
     return 0;
    }

    这里写图片描写叙述
    树建立起来
    这里写图片描写叙述
    那么研究一下这个栈的调用过程

    利用vs2008的反汇编窗体来看
    1、进入main第一次调用函数
    tree_front(T);
    0084162E lea eax,[T]
    00841631 push eax
    00841632 call tree_front (8410B4h) //称为函数1
    00841637 add esp,4
    将T的地址传入函数
    2、第一次进入函数1后
    输入A,在在调用自身函数之前
    tree_front(T->left);
    0084156D mov eax,dword ptr [T]
    00841570 mov ecx,dword ptr [eax]
    00841572 add ecx,4
    00841575 push ecx
    00841576 call tree_front (8410B4h) //称为函数2
    0084157B add esp,4
    将A地址压栈
    3、进入递归调用函数2
    输入B。在在调用自身函数之前
    tree_front(T->left);
    0084156D mov eax,dword ptr [T]
    00841570 mov ecx,dword ptr [eax]
    00841572 add ecx,4
    00841575 push ecx
    00841576 call tree_front (8410B4h) //称为函数3
    0084157B add esp,4
    将B压栈
    4、进入递归调用函数3
    输入×,表示输入NULL
    T=NULL;
    00841591 mov eax,dword ptr [T]
    00841594 mov dword ptr [eax],0
    返回调用函数3的地方
    运行下一条指令
    0084157B add esp,4
    就是出栈,将B出栈
    准备进入右子树调用函数
    5、进入递归右子树调用函数
    tree_front(T->right);
    0084157E mov eax,dword ptr [T]
    00841581 mov ecx,dword ptr [eax]
    00841583 add ecx,8
    00841586 push ecx
    00841587 call tree_front (8410B4h) //称为函数4
    0084158C add esp,4
    此时又将B压栈
    6、进入递归调用函数4
    输入×,表示输入NULL
    T=NULL;
    00841591 mov eax,dword ptr [T]
    00841594 mov dword ptr [eax],0
    返回函数4,运行下一条指令
    0084158C add esp,4
    将B出栈,这时,将从函数2出来,进入函数2的下一条右子树的调用
    7、进入函数2的下一条右子树的调用
    输入×。运行
    T=NULL;
    00841591 mov eax,dword ptr [T]
    00841594 mov dword ptr [eax],0
    然后退出函数,运行下一条指令、

    。。。
    知道最后完毕
    用一个调用图来表示
    这里写图片描写叙述
    分析一下这个栈的情况
    无论是节点A还是节点B。在进入左子树时,都会将本节点的地址保存起来,以便返回
    所以可以用vector容器来模拟这个压入过程

    初始一些变量
    struct treenode;
    typedef struct treenode* TREE;

    struct treenode
    {
    char data;
    TREE left;
    TREE right;
    };

    则原始非递归前序遍历的伪代码为

    void preorder(TREE T)
    {
    while(1)
    {
    訪问节点T。
    压栈T。
    T=T->left(T变成将左子树)
    }
    }
    完毕上图A到B的遍历。
    B的做节点为NULL这时,应该pop出B节点
    然后将B的右子树作为新的节点
    然后改动伪代码
    void preorder(TREE T)
    {
    while(1)
    {
    if(T!=NULL)
    {
    訪问节点T;
    压栈T;
    T=T->left(T变成将左子树)
    }
    else
    {
    T=出栈;
    T=T->right;
    }
    }
    }
    出栈就是出如今遍历到左子树为NULL时,依据最新入栈的节点,切换到右子树
    右子树为NULL时回到父树的父树。然后变成,父树父树的右子树。

    再考虑,while的结束条件,观察图中,A的右子树返回时。遍历结束。此时栈中不再包括A、B的节点,
    改动伪代码
    void preorder(TREE T)
    {
    初始化栈;
    while(栈不为空)
    {
    if(T!=NULL)
    {
    訪问节点T;
    压栈T。
    T=T->left(T变成将左子树)
    }
    else
    {
    T=出栈;
    T=T->right;
    }
    }
    }
    条件还是有个bug,当A左子树返回时,栈就为空了。可是此时还要遍历A的右子树

    看看整个树遍历时,栈的情况
    这里写图片描写叙述
    栈空有三处,所以还要加上一个条件
    这里有的变量就是节点,所以上次栈空时相应的节点情况
    有效->有效->空(最后一个右子树)
    而要退出是在最后一个栈空时,依据逻辑推断。两者相与
    改动伪代码
    void preorder(TREE T)
    {
    初始化栈。
    while(栈不为空 || 节点不为NULL)
    {
    if(T!=NULL)
    {
    訪问节点T;
    压栈T;
    T=T->left(T变成将左子树)
    }
    else
    {
    T=出栈;
    T=T->right;
    }
    }
    }

    所以代码实现为

    
    void preorder(TREE T)
    {
     vector <TREE> stack;
     //vector <char> view;
     while(T!=NULL || stack.size()!=0)
     {
      if(T!=NULL)
      {
       cout<<T->data<<endl;
       stack.push_back(T);
       //view.push_back(T->data);
       T=T->left;
      }
      else
      {
       T=stack.back();
       stack.pop_back();
      // view.pop_back();
       T = T->right;
      }
     }
    }

    整个測试代码为(包括递归遍历。作为比較)

    #include <iostream>
    #include <vector>
    using namespace std;
    
    struct treenode;
    typedef struct treenode* TREE;
    
    struct treenode
    {
     char data;
     TREE left;
     TREE right;
    };
    void tree_front(TREE& T)
    {
     char buff;
     cout<<"please input ,or NULL(*)"<<endl;
     cin>>buff;
     if(buff!='*')
     {
      T=new struct treenode;
      if(T==NULL)
       exit(-1);
      T->data=buff;
      tree_front(T->left);
      tree_front(T->right);
    
     }
     else
      T=NULL;
    }
    
    void preorder(TREE T)
    {
     vector <TREE> stack;
     vector <char> view;
     while(T!=NULL || stack.size()!=0)
     {
      if(T!=NULL)
      {
       cout<<T->data<<endl;
       stack.push_back(T);
       view.push_back(T->data);
       T=T->left;
      }
      else
      {
       T=stack.back();
       stack.pop_back();
       view.pop_back();
       T = T->right;
      }
     }
    }
    
    void  pre_compare(TREE T)
    {
     if(T!=NULL)
     {
      cout<<T->data<<endl;
      pre_compare(T->left);
      pre_compare(T->right);
     }
    }
    
    int main()
    {
     TREE T;
     tree_front(T);
     preorder(T);
     cout<<"follow is to compare"<<endl;
     pre_compare(T);
    
    
     return 0;
    }
    
  • 相关阅读:
    C/C++中volatile关键字详解(转)
    Spring中 @Autowired标签与 @Resource标签 的区别(转)
    [转]各种互斥量的总结
    nginx限制ip访问(转)
    HDU 4833 Best Financing (DP)
    HDU 4832 Chess (DP)
    HDU 4831 Scenic Popularity
    POJ 2155 Matrix (二维线段树)
    POJ 2155 Matrix (二维树状数组)
    HDU 4819 Mosaic (二维线段树)
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/7147900.html
Copyright © 2011-2022 走看看