数据结构之二叉树的遍历

前言:在上一篇中介绍了二叉树的基础知识,这一篇介绍遍历二叉树的实现

一、二叉树的存储结构:

        二叉树的存储结构可以采用顺序存储,也可以采用链式存储,其中链式存储更加灵活。

        在链式存储结构中,与线性链表类似,二叉树的每个结点采用结构体表示,结构体包含三个域:数据域、左指针、右指针。

二、二叉树的遍历:

二叉树实现需要用到的数据结构代码如下:

struct BiTNode{     //定义二叉树
	char data;     //每个结点的数据
	BiTNode *lchild, *rchild;     //左右孩子指针
};

struct Stack{     //定义栈
	int top;       //栈顶指针
	BiTNode *stacksize[100];      //栈的容量
};

void InitStack(Stack &S)    //初始化一个空栈
{
	S.top = -1;
}

int Push(Stack &S, BiTNode *pt)     //元素进栈
{
	S.stacksize[++S.top] = pt;
	return 1;
}

BiTNode * Pop(Stack &S)   //元素出栈
{
	BiTNode *pt;
	pt = S.stacksize[S.top--];
	return pt;
}

BiTNode * GetTop(Stack S)      //获取栈顶元素
{
	BiTNode *pt;
	if (S.top == -1)
		return 0;
	else
	{
		pt = S.stacksize[S.top];
		return pt;
	}
}

int IsEmptyStack(Stack S)   //判断是否空栈,空栈返回1,否则返回0
{
	return S.top == -1;
}


 二叉树遍历通常借用“栈”这种数据结构实现,有两种方式:递归方式及非递归方式。

(1)在递归方式中,栈是由操作系统维护的,用户不必关心栈的细节操作,用户只需关心“访问顺序”即可。因而,采用递归方式实现二叉树的遍历比较容易理解,算法简单,容易实现。

(2)借用“栈”采用非递归方式,也能实现遍历。但是,这时的栈操作(push、pop等)是由用户进行的,因而实现起来会复杂一些,而且也不容易理解,但有助于我们对树结构的遍历有一个深刻、清晰的理解。

(a)非递归先序遍历:

在遍历某一个二叉(子)树时,以一当前指针记录当前要处理的二叉(左子)树,以一个栈保存当前树之后处理的右子树。首先访问当前树的根结点数据,接下来应该依次遍历其左子树和右子树,然而程序的控制流只能处理其一,所以考虑将右子树的根保存在栈里面,当前指针则指向需先处理的左子树,为下次循环做准备;若当前指针指向的树为空,说明当前树为空树,不需要做任何处理,直接弹出栈顶的子树,为下次循环做准备,代码如下

int PreOrderTraverse(BiTNode *T)       //二叉树先序遍历的非递归算法
{
	Stack S;
	InitStack(S);
	BiTNode *pt = NULL;
	pt = T;
	while (pt != NULL || IsEmptyStack(S)!=1)
	{
		if (pt != NULL)
		{
			cout << pt->data;
			Push(S, pt->rchild);  //右子树进栈,让当前指针指向左子树,为下一个循环做准备
			pt = pt->lchild;
		}
		else
			pt=Pop(S);  //左子树为空,不做任何处理,直接弹出右子树,为下一个循环准备
	}
	return 1;    //遍历完成
}

相对于非递归先序遍历,非递归的中序/后序遍历稍复杂一点。

(b)非递归中序遍历:

若当前树不为空树,则访问其根结点之前应先访问其左子树,因而先将当前根节点入栈,然后考虑其左子树,不断将非空的根节点入栈,直到左子树为一空树;当左子树为空时,不需要做任何处理,弹出并访问栈顶结点,然后指向其右子树,为下次循环做准备。代码如下:

int InOrderTraverse(BiTNode *T)   //二叉树中序非递归遍历
{
	Stack S;
	InitStack(S);
	BiTNode *pt = NULL;
	pt = T;
	while (pt != NULL || IsEmptyStack(S)!=1)
	{
		if (pt != NULL)
		{
			Push(S, pt);    //访问根节点之前先访问左结点,所以现将根节点进栈
			pt = pt->lchild;
		}
		else        //根指针退栈,访问根节点,遍历右子树
		{
			pt=Pop(S);
			cout << pt->data;
			pt = pt->rchild;     //访问根节点之后,访问右子树
		}
	}
	return 1;
}

(c)后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问,并且左孩子在右孩子之前访问才能访问根结f点,这就为流程控制带来了难题。下面介绍一种思路。

     要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点p,先将其入栈。若p不存在左孩子和右孩子,则可以直接访问它,或者p存在左孩子或右孩子,但是其左孩子和右孩子都已经被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将p的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子之前别访问,左孩子和右孩子都在根结点前面被访问。代码如下:

int PostOrderTraverse(BiTNode *T)       //二叉树的后序非递归算法
{
	Stack S;
	InitStack(S);
	BiTNode *pt = NULL;
	pt = T;
	BiTNode *pre, *cur;
	pre = NULL;
	if (!pt)             //空树
	{
		cout << "the tree is null" << endl;
		return 0;
	}
	Push(S, pt);
	while (IsEmptyStack(S)!=1)
	{
		cur =GetTop(S);  //获取栈顶元素
		if ((cur->lchild == NULL&&cur->rchild == NULL) || (pre != NULL && (pre == cur->lchild || pre == cur->rchild)))
		{
			//如果当前结点没有孩子或者左右孩子都已经访问过,就直接访问该结点
			cout << cur->data;
			Pop(S);
			pre = cur;       //标识它成为上一个被访问的元素
		}
		else
		{
			if (cur->rchild != NULL)      //当前结点既有左右孩子没有被访问,那么就该结点就进栈
				Push(S, cur->rchild);
			if (cur->lchild != NULL)
				Push(S, cur->lchild);
		}
	}
	return 1;
}


三、完整程序代码:

#include "stdafx.h"
#include <iostream>

using namespace std;

struct BiTNode{     //定义二叉树
	char data;     //每个结点的数据
	BiTNode *lchild, *rchild;     //左右孩子指针
};

struct Stack{     //定义栈
	int top;       //栈顶指针
	BiTNode *stacksize[100];      //栈的容量
};

void InitStack(Stack &S)    //初始化一个空栈
{
	S.top = -1;
}

int Push(Stack &S, BiTNode *pt)     //元素进栈
{
	S.stacksize[++S.top] = pt;
	return 1;
}

BiTNode * Pop(Stack &S)   //元素出栈
{
	BiTNode *pt;
	pt = S.stacksize[S.top--];
	return pt;
}

BiTNode * GetTop(Stack S)      //获取栈顶元素
{
	BiTNode *pt;
	if (S.top == -1)
		return 0;
	else
	{
		pt = S.stacksize[S.top];
		return pt;
	}
}

int IsEmptyStack(Stack S)   //判断是否空栈,空栈返回1,否则返回0
{
	return S.top == -1;
}

int PreOrderTraverse(BiTNode *T)       //二叉树先序遍历的非递归算法
{
	Stack S;
	InitStack(S);
	BiTNode *pt = NULL;
	pt = T;
	while (pt != NULL || IsEmptyStack(S)!=1)
	{
		if (pt != NULL)
		{
			cout << pt->data;
			Push(S, pt->rchild);  //右子树进栈,让当前指针指向左子树,为下一个循环做准备
			pt = pt->lchild;
		}
		else
			pt=Pop(S);  //左子树为空,不做任何处理,直接弹出右子树,为下一个循环准备
	}
	return 1;    //遍历完成
}

int InOrderTraverse(BiTNode *T)   //二叉树中序非递归遍历
{
	Stack S;
	InitStack(S);
	BiTNode *pt = NULL;
	pt = T;
	while (pt != NULL || IsEmptyStack(S)!=1)
	{
		if (pt != NULL)
		{
			Push(S, pt);    //访问根节点之前先访问左结点,所以现将根节点进栈
			pt = pt->lchild;
		}
		else        //根指针退栈,访问根节点,遍历右子树
		{
			pt=Pop(S);
			cout << pt->data;
			pt = pt->rchild;     //访问根节点之后,访问右子树
		}
	}
	return 1;
}
int PostOrderTraverse(BiTNode *T)       //二叉树的非递归算法
{
	Stack S;
	InitStack(S);
	BiTNode *pt = NULL;
	pt = T;
	BiTNode *pre, *cur;
	pre = NULL;
	if (!pt)
	{
		cout << "the tree is null" << endl;
		return 0;
	}
	Push(S, pt);
	while (IsEmptyStack(S)!=1)
	{
		cur =GetTop(S);  //获取栈顶元素
		if ((cur->lchild == NULL&&cur->rchild == NULL) || (pre != NULL && (pre == cur->lchild || pre == cur->rchild)))
		{
			//如果当前结点没有孩子或者左右孩子都已经访问过,就直接访问该结点
			cout << cur->data;
			Pop(S);
			pre = cur;       //标识它成为上一个被访问的元素
		}
		else
		{
			if (cur->rchild != NULL)      //当前结点既有左右孩子没有被访问,那么就该结点就进栈
				Push(S, cur->rchild);
			if (cur->lchild != NULL)
				Push(S, cur->lchild);
		}
	}
	return 1;
}

int CreateBiTree(BiTNode *&T)   //采用先序遍历法创建二叉树
{
	char c;
	c = getchar();
	if (c == '#')    //  空树
		T = NULL;
	else
	{
		T = new BiTNode;
		T->data = c;
		CreateBiTree(T->lchild);
		CreateBiTree(T->rchild);
	}
	return 1;
}

int _tmain(int argc, _TCHAR* argv[])   
{
	BiTNode *T = NULL;
	cout << "先序输入字符创建一个二叉树:" << endl;
	CreateBiTree(T);
	cout << "前序遍历二叉树序列为:" << endl;
	PreOrderTraverse(T);
	cout << endl;
	cout << "中序遍历二叉树序列为:" << endl;
	InOrderTraverse(T);
	cout << endl;
	cout << "后序遍历二叉树序列为:" << endl;
	PostOrderTraverse(T);
	cout << endl;
	return 0;
}

参考:严蔚敏《数据结构》(c语言版)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值