您的位置:首页 > 其它

二叉树的建立以及六种递归非递归遍历

2014-05-06 18:14 288 查看
最近在看数据结构,在看到二叉树的这一章,发现书上详细写出了三种遍历方法,但是对于非递归的实现却介绍的不是很详细,但是在笔试或者面试的时候经常被问到二叉树的非递归实现遍历,仔细的思考了一下,实现了二叉树的三种递归遍历以及三种非递归遍历。

一、二叉树的建立

二叉树的一些很详细的知识在此不做介绍,二叉树的每一个节点最多有两个子孩子,在建立二叉树的时候需要注意的是对于每一个子孩子为空的二叉树,在用键盘输入的时候分别要给输入相应的空符号,例如现在要建立一个二叉树,如下图所示:



在建立如上图的二叉树的时候,一定要注意输入NULL节点,这一点对于初学者很容易造成疑惑,因为只有八个节点,为什么我输入八个节点之后,显示出来的二叉树不是我想要的二叉树,原因就是NULL节点的输入以及输入的顺序,如本例的图所示,在此假设当输入的数是-1的时候代表的是NULL节点,那么应该输入的顺序就应该是:1,2,4,-1,-1,5,-1,-1,3,6,-1,-1,7,-1,8,-1,-1;具体的代码如下所示:
typedef struct treenode
{
int data;
struct treenode *left,*right;
}treenode,*mytree;

void creattree(mytree &a)
{
int temp;
cin>>temp;
if(temp==-1)
a=NULL;
else
{
a=(mytree)malloc(sizeof(treenode));
a->data=temp;
creattree(a->left);
creattree(a->right);
}
}
因为很简单,所以在此不做详细的解释,唯一的一点说明的是,因为是指针操作,所以对于一些读者来说可能会有点问题,在这里所采用的传参方式是引用传参,可能相对于指针来说会简单点,因为如果是指针传地址的话就应该如下所示:

void creattree1(mytree *a)
{
int temp;
cin>>temp;
if(temp==-1)
*a=NULL;
else
{
(*a)=(mytree)malloc(sizeof(treenode));
(*a)->data=temp;
creattree1(&(*a)->left);
creattree1(&(*a)->right);
}
}


只要注意这里传输的是地址,但是对于地址的操作有这两种方法,个人推荐用值传递,方便简单。

二、二叉树的三种遍历方式

(1)前序遍历

二叉树的前序遍历是先访问根节点,接着访问左孩子,最后访问右孩子,二叉树的三种遍历方式可能有时候会比较容易弄混淆,但是只要记住两点:
1,左孩子比右孩子先访问
2,三种遍历方式所说的是访问父母节点的顺序,比如说前序遍历就是首先访问父母节点,中序遍历就是第二个访问父母节点,后序遍历就是最后访问父母节点
记住这两点,基本上就不会将三种遍历再记混淆,关于前序遍历的递归方式首先访问父母节点,然后访问左孩子,访问右孩子,以此递归就可以,很好理解。前序遍历的非递归实现方法是靠栈的方法实现的,首先如果节点不为空,打印节点的信息,同时将此节点入栈,将节点的左孩子的值赋给节点,一直寻找左孩子,直到左孩子为空,若左孩子为空,取栈顶的节点,并进行出栈操作,并将栈顶节点的右孩子赋值给当前节点,一直到栈为空,遍历结束,具体的实现代码如下所示:

void pretravel_feidiedai(const mytree a)
{
stack<mytree> s;
mytree temp=a;
while(temp!=NULL||!s.empty())
{
while(temp!=NULL)
{
cout<<temp->data<<" ";
s.push(temp);
temp=temp->left;
}
if(!s.empty())
{
temp=s.top();
s.pop();
temp=temp->right;
}
}
}


(2)中序遍历

中序遍历的非递归方法和前序遍历的类似具体的步骤如下所示:
1,若节点不为空,将左孩子节点赋给当前节点值
2,若左孩子不为空,重复1操作,若左孩子为空,取栈顶元素,访问栈顶元素,出栈,并将取出的栈顶元素的右孩子的值赋给当前节点,重复1操作
3,若栈为空结束。
代码如下所示:

void midtravel_feidiedai(const mytree a)
{
stack<mytree> s;
mytree temp=a;
while(temp!=NULL||!s.empty())
{
while(temp!=NULL)
{
s.push(temp);
temp=temp->left;
}
if(!s.empty())
{
temp=s.top();
cout<<temp->data<<" ";
s.pop();
temp=temp->right;
}
}
}


(3)后序遍历

后续遍历相比与前序遍历以及中序遍历有点不同,后续遍历需要先访问左孩子,然后访问右孩子,然后访问父母节点,当
情况1:当前节点的左孩子以及右孩子同时为空的时候,才会将当前节点值输出
情况2::当前一个访问的是当前节点的左孩子或者右孩子的时候,那么此时也会将当前节点的值输出
具体的思路就是上述所表述的,具体的步骤如下:
(1)初始化,将根节点入栈,将前一个访问的置为NULL;
(2)如果栈不为空,取栈顶元素作为当前节点的值,判断当前节点是否满足两种输出情况,如果满足输出,出栈,将当前节点的值赋给pre,即前一个访问节点,进行下次循环;如果不满足进行步骤3
(3)如果当前节点的左孩子以及右孩子都不为空,现将右孩子入栈,再入栈左孩子;如果一个为空,那么入栈不为空的
(4)循环(2)到(3),直至栈为空的时候截止。
在这里面值的注意的是步骤三的入栈情况,是先右孩子入栈,然后才是左孩子入栈,因为栈是LIFO的结构,具体的实现代码如下所示:

void posttravel_feidiedai(const mytree a)
{
stack<mytree> s;
mytree cur=a,pre=NULL;
s.push(a);
while(!s.empty())
{
cur=s.top();
if((cur->right==NULL&&cur->left==NULL)||((pre!=NULL)&&(pre==cur->left||pre==cur->right)))
{
cout<<cur->data<<" ";
s.pop();
pre=cur;
}
else
{
if(cur->right!=NULL)
s.push(cur->right);
if(cur->left!=NULL)
s.push(cur->left);
}
}


三、递归与非递归的完整代码

上述已经将非递归的情况说明清楚,递归的情况基本上每本书都会涉及,下面将所有的代码贴上去:
头文件 tree.h

#ifndef _TREE_H
#define _TREE_H
#include<iostream>
#include<stack>
#include<windows.h>
using namespace std;
typedef struct treenode
{
int data;
struct treenode *left,*right;
}treenode,*mytree;
void creattree(mytree &a);
void pretravel_diedai(const mytree a);
void midtravel_diedai(const mytree a);
void posttravel_diedai(const mytree a);
void pretravel_feidiedai(const mytree a);
void midtravel_feidiedai(const mytree a);
void posttravel_feidiedai(const mytree a);
#endif


实现代码 tree.cpp

#include"tree.h"
void creattree(mytree &a)
{
int temp;
cin>>temp;
if(temp==-1)
a=NULL;
else
{
a=(mytree)malloc(sizeof(treenode));
a->data=temp;
creattree(a->left);
creattree(a->right);
}
}

void creattree1(mytree *a) { int temp; cin>>temp; if(temp==-1) *a=NULL; else { (*a)=(mytree)malloc(sizeof(treenode)); (*a)->data=temp; creattree1(&(*a)->left); creattree1(&(*a)->right); } }

void pretravel_diedai(const mytree a)
{
if(a!=NULL)
{
cout<<a->data<<" ";
pretravel_diedai(a->left);
pretravel_diedai(a->right);
}
}
void midtravel_diedai(const mytree a)
{
if(a!=NULL)
{
midtravel_diedai(a->left);
cout<<a->data<<" ";
midtravel_diedai(a->right);
}
}
void posttravel_diedai(const mytree a)
{
if(a!=NULL)
{
posttravel_diedai(a->left);
posttravel_diedai(a->right);
cout<<a->data<<" ";
}
}
void pretravel_feidiedai(const mytree a) { stack<mytree> s; mytree temp=a; while(temp!=NULL||!s.empty()) { while(temp!=NULL) { cout<<temp->data<<" "; s.push(temp); temp=temp->left; } if(!s.empty()) { temp=s.top(); s.pop(); temp=temp->right; } } }
void midtravel_feidiedai(const mytree a) { stack<mytree> s; mytree temp=a; while(temp!=NULL||!s.empty()) { while(temp!=NULL) { s.push(temp); temp=temp->left; } if(!s.empty()) { temp=s.top(); cout<<temp->data<<" "; s.pop(); temp=temp->right; } } }
void posttravel_feidiedai(const mytree a) { stack<mytree> s; mytree cur=a,pre=NULL; s.push(a); while(!s.empty()) { cur=s.top(); if((cur->right==NULL&&cur->left==NULL)||((pre!=NULL)&&(pre==cur->left||pre==cur->right))) { cout<<cur->data<<" "; s.pop(); pre=cur; } else { if(cur->right!=NULL) s.push(cur->right); if(cur->left!=NULL) s.push(cur->left); } }

}


主函数main代码如下:

#include"tree.h"
void tree_6_travel();
int main()
{
tree_6_travel();
return 0;
}
void tree_6_travel()
{
mytree tree1;
creattree(tree1);
cout<<"迭代前序:"<<endl;
pretravel_diedai(tree1);
cout<<endl<<"迭代中序:"<<endl;
midtravel_diedai(tree1);
cout<<endl<<"迭代后序:"<<endl;
posttravel_diedai(tree1);
cout<<endl<<"非迭代前序:"<<endl;
pretravel_feidiedai(tree1);
cout<<endl<<"非迭代中序:"<<endl;
midtravel_feidiedai(tree1);
cout<<endl<<"非迭代后序:"<<endl;
posttravel_feidiedai(tree1);
system("pause");
}


截图如下所示:



参考内容:(1)/article/4719934.html
(2)数据结构(c语言版) 严蔚敏
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: