首页 > 代码库 > 重现二叉树非递归算法的构建过程

重现二叉树非递归算法的构建过程

递归完毕树的遍历非常好理解,倘若是非递归。不要告诉我算法导论上有,我要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=http://www.mamicode.com/buff;"hljs-keyword">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=http://www.mamicode.com/buff;"hljs-keyword">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;
}
<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

重现二叉树非递归算法的构建过程