超详细 C/C++二叉树的前序中序后序遍历——递归和非递归实现 算法 !

二叉树的遍历

  • 二叉树的递归遍历就是按照前中后序的遍历思想不断调用来进行的;
  • (难点) 二叉树的非递归遍历:
    • 前序和中序的非递归遍历,只需要用一个栈和定义来实现即可;
    • 而二叉树的后序非递归遍历,之前思考了挺久的,大多数的相关算法都是用对结点做一个标志表示第几次访问该结点来进行非递归后序遍历,我觉得写起来比较麻烦 (懒)
    • 我觉得可以用前序遍历的思想来做后序遍历,后序:左—右—根 ——> 根—右—左, 把后者遍历过程中的需要输出的数据存放在一个栈中,最后弹栈即可;

详见代码

Algorithm

#include <iostream>
#include <stack>

using namespace std;

typedef struct BitNode {				//创建二叉树的结构体;
	char data;
	BitNode *lchild, *rchild;
	BitNode(char ch)					//添加创建新结点的构造函数;
	{
		data = ch;
		lchild = NULL;
		rchild = NULL;
	}
}*Tree, BitNode;

void CreateTree(Tree &p)				//先序遍历创建二叉树;
{
	char ch;
	cin >> ch;							//输入字符;
	if (ch == '#')						//判断字符是否为结束当前结点的创建;
		p = NULL;
	else {
		p = new BitNode(ch);			//调用构造函数创建一个新结点,并且数据域赋值为ch;
		CreateTree(p->lchild);			//创建左结点;
		CreateTree(p->rchild);			//创建右结点;
	}
}

void PreOrder(Tree &p)					//递归前序遍历二叉树;
{
	if (p) {							//判断当前结点是否是空;
		cout << p->data << " ";			//先输出当前结点的数据域的值;
		PreOrder(p->lchild);			//遍历当前结点的左子树;
		PreOrder(p->rchild);			//遍历当前结点的右子树;
	}
}

void InOrder(Tree &p)					//递归中序遍历二叉树
{
	if (p) {							//判断当前结点是否是空;
		InOrder(p->lchild);				//遍历当前结点的左子树;
		cout << p->data << " ";			//输出当前结点的数据域的值;
		InOrder(p->rchild);				//遍历当前结点的右子树;
	}
}

void PostOrder(Tree &p)					//递归后续遍历二叉树
{
	if (p) {							//判断当前结点是否是空;
		PostOrder(p->lchild);			//遍历当前结点的左子树;
		PostOrder(p->rchild);			//遍历当前结点的右子树;
		cout << p->data << " ";			//输出当前结点的数据域的值;
	}
}
/***********************重点是非递归遍历*************************/
void F_PreOrder(Tree &p)				//非递归前序遍历二叉树;
{
	stack <Tree> s;						//声明一个用来存树结点的栈;
	Tree T = p;							//把二叉树的根节点赋值给T;
	while (T || !s.empty())				//设置循环并以T和栈都为空时为结束循环标志;
	{
		if (T) {						//如果T不是空;
			cout << T->data << " ";		//因为是先序遍历,所以先输出T结点的数据域的值;
			s.push(T);					//把T结点入栈,为的是保存T结点的右子树的位置;
			T = T->lchild;				//访问T结点的左子树;
		}
		else {
			T = s.top()->rchild;		//如果T为空,说明上个结点的左子树是空的,此时需要访问上个节点的右子树;
			s.pop();					//弹出栈顶元素,即上个节点;
		}
	}
}

void F_InOrder(Tree &p)					//非递归中序遍历二叉树;
{
	stack <Tree> s;						//声明一个用来存树结点的栈;
	Tree T = p;							//把二叉树的根节点赋值给T;
	while (T || !s.empty())				//设置循环并以T和栈都为空时为结束循环标志;
	{
		if (T) {						//如果T不是空;
			s.push(T);					//把T结点入栈,为的是保存T结点的右子树的位置;
			T = T->lchild;				//访问T结点的左子树;
		}
		else {							//如果T为空;
			T = s.top();				//访问当前结点的父结点,即栈顶结点;
			cout << T->data << " ";		//因为是中序遍历,所以此时应该输出该结点的数据域的数据;
			T = T->rchild;				//接着访问当前结点的右子树;
			s.pop();					//弹出栈顶元素;
		}
	}
}
/*************************************************************************************************************************************/
/***PS:非递归后序相对前序和中序比较复杂,但是相比对结点做一个标志表示第几次访问该结点来进行非递归后序遍历,我觉得可以有一个更简单更易理解的方法***/
/*************************************************************************************************************************************/
void F_PostOrder(Tree &p)				//非递归后序遍历二叉树;
{
	stack <Tree> s;						//声明一个用来存树结点的栈;
	stack <char> ss;					//声明一个来存放需要输出的数据的数据栈;
	Tree T = p;							//把二叉树的根节点赋值给T;
	while (T || !s.empty())				//设置循环并以T和栈都为空时为结束循环标志;
	{
		if (T) {						//如果T不是空;
			ss.push(T->data);			//把当前结点的数据压入数据栈;
			s.push(T);					//把当前的结点压入结点栈,为的是保存左子树的位置;
			T = T->rchild;				//访问T结点的右子树;
		}
		else {							//如果T为空;
			T = s.top()->lchild;		//访问当前结点的父结点的左子树(这样就相当于最后访问的左子树了);
			s.pop();					//弹出栈顶元素;
		}
	}
	while (!ss.empty())					//按顺序输出数据栈,输出顺序就是后序遍历的顺序了;
	{
		cout << ss.top() << " ";
		ss.pop();
	}
/**********相信认真看完代码的同学都猜到了,对的,我就是用前序遍历的方法来遍历后序,左——右——根 ——> 根——右——左 (虽然也不是很高明,但是理解起来更简单)**********/
}					

int main()
{
	Tree p = NULL;

	CreateTree(p);

	cout << "递归法先序遍历二叉树: ";
	PreOrder(p);
	cout << endl;
	cout << "递归法中序遍历二叉树: ";
	InOrder(p);
	cout << endl;
	cout << "递归法后序遍历二叉树: ";
	PostOrder(p);
	cout << endl;

	cout << "非递归先序遍历二叉树: ";
	F_PreOrder(p);
	cout << endl;
	cout << "非递归中序遍历二叉树: ";
	F_InOrder(p);
	cout << endl;
	cout << "非递归后序遍历二叉树: ";
	F_PostOrder(p);
	cout << endl;
	return 0;
}

蒟蒻一只,欢迎指正。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值