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

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

一、二叉树的建立

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



在建立如上图的二叉树的时候,一定要注意输入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)http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html

    (2)数据结构(c语言版) 严蔚敏

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值