标签:二叉树
递归完成树的遍历很好理解,倘若是非递归,不要告诉我算法导论上有,我要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;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:二叉树
原文地址:http://blog.csdn.net/u010442328/article/details/46926425