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

Posted slgkaifa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重现二叉树非递归算法的构建过程相关的知识,希望对你有一定的参考价值。

递归完毕树的遍历非常好理解,倘若是非递归。不要告诉我算法导论上有,我要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;
}





























































































































































以上是关于重现二叉树非递归算法的构建过程的主要内容,如果未能解决你的问题,请参考以下文章

递归遍历二叉树

创建二叉树非递归完成对二叉树的先序和后序遍历并遍历输出每一层的结点数查找结点P 和结点Q的最近共同祖先

二叉树非递归遍历

非递归建立二叉树

二叉树的非递归遍历

二叉树非递归遍历