二叉树遍历_递归和迭代(动图)

1. 定义二叉树

  首先定义一个简单的二叉树,以供测试:

template<class T>
struct BinaryNode
{
	BinaryNode<T>* _left;
	BinaryNode<T>* _right;
	T _val;

	BinaryNode(const T& val = T())
		:_left(nullptr)
		, _right(nullptr)
		, _val(val)
	{}
};

void InitABinaryTree(BinaryNode<char>*& Proot)
{
	Proot = new BinaryNode<char>('a');
	Proot->_left = new BinaryNode<char>('b');
	Proot->_right = new BinaryNode<char>('c');
	Proot->_left->_left = new BinaryNode<char>('d');
	Proot->_right->_left = new BinaryNode<char>('e');
	Proot->_right->_right = new BinaryNode<char>('f');
}

  为了看清这个二叉树的整体架构,可以使用Graphviz(快速掌握)打印出来,贫道写了一个demo以供参考:

#include <iostream>

//前序遍历
void WriteTree2File(FILE* pFile, BinaryNode<char>* root, int& iNum) // iNum记录当前递归的次数,用以区分Node
{
	if (root == nullptr)
	{
		//fputs("\t Null%d[label=\"null\"]\n", pFile);
		return;
	}

	if (root->_left != nullptr)
	{
		fprintf(pFile, "\t%c -> %c[weight = 30]\n", root->_val, root->_left->_val);
		fprintf(pFile, "\tMidNode%d[shape = point, style=invis]\n", iNum);  //设置一个占位node,使成图正
		fprintf(pFile, "\t%c -> MidNode%d[weight=30, style=invis]\n", root->_val, iNum);
	}
	else // 打印空节点,若不想打印空节点,则删除else即可
	{
		fprintf(pFile, "\t NullL%d[label=\"null\"]\n", iNum);
		fprintf(pFile, "\t%c -> NullL%d[weight = 30]\n", root->_val, iNum);
		fprintf(pFile, "\tMidNode%d[shape = point, style=invis]\n", iNum);  //设置一个占位node,使成图正
		fprintf(pFile, "\t%c -> MidNode%d[weight=10, style=invis]\n", root->_val, iNum);
	}

	if (root->_right != nullptr)
	{
		fprintf(pFile, "\t%c -> %c[weight = 30]\n", root->_val, root->_right->_val);
	}
	else
	{
		fprintf(pFile, "\t NullR%d[label=\"null\"]\n", iNum);
		fprintf(pFile, "\t%c -> NullR%d[weight = 30]\n", root->_val, iNum);
	}
	iNum++;

	WriteTree2File(pFile, root->_left, iNum);
	WriteTree2File(pFile, root->_right, iNum);
}

int main()
{
    //构造一个二叉树
	BinaryNode<char> *Proot = nullptr;
	InitABinaryTree(Proot);
	FILE* pFile = fopen("BinaryTree.dot", "w+"); //将Graphviz的代码写到文件中
	if (pFile == nullptr)
	{
		perror("Create File Failed!\n");
		return -1;
	}

	int i = 0;
	fputs( "digraph Graph{ \n", pFile);
	WriteTree2File(pFile, Proot, i);
	fputs( "}\n",pFile);
	fclose(pFile);

	system("dot -Tpng -O BinaryTree.dot"); //Graphviz编译
	system("BinaryTree.dot.png");
	return 0;
}

结果显示:
在这里插入图片描述

2. 二叉树递归遍历

  二叉树的递归遍历比较简单,根据节点访问的顺序分为前序、中序、后序遍历。要理解递归的过程,可以展开递归的栈。

// 前序
void PreOerder(BinaryNode<char>* root)
{
	if (root == nullptr)
		return;
	std::cout << root->_val << " "; //书写位置不同,可以实现遍历顺序的改变
	PreOerder(root->_left);
	PreOerder(root->_right);
}
// 中序
void PreOerder(BinaryNode<char>* root)
{
	if (root == nullptr)
		return;
	PreOerder(root->_left);
	std::cout << root->_val << " ";
	PreOerder(root->_right);
}
// 后序
void PreOerder(BinaryNode<char>* root)
{
	if (root == nullptr)
		return;
	PreOerder(root->_left);
	PreOerder(root->_right);
	std::cout << root->_val << " ";
}

以前序为例,画出展开图,为了更好看到递归的过程,将代码改写:

// 前序
void PreOerder(BinaryNode<char>* root, std::vector<char> &vec, int &num)
{
	if (root == nullptr)
		return;
	vec.push_back(root->_val);
	std::cout << num++ << " ";
	PreOerder(root->_left, vec, num);
	PreOerder(root->_right, vec, num);
}

作展开图:

在这里插入图片描述

3. 二叉树迭代遍历

3.1 前序遍历

  迭代法需要用到栈或者队列,下面先看运行的动图(图中贴了个二维码防盗,哈哈~~,还没有写多少东西的公众号㊣):

在这里插入图片描述

根据图写出代码:

void PreOrderStack(BinaryNode<char>* root, std::vector<char>& vec)
{
	if (root == nullptr)
		return;
	std::stack< BinaryNode<char>*> st;
	st.push(root);
	while (!st.empty())
	{
		BinaryNode<char>* tp = st.top();
		vec.push_back(tp->_val);
		st.pop();
		
		if (tp->_right != nullptr)
			st.push(tp->_right);
		if (tp->_left != nullptr)
			st.push(tp->_left);
	}
}

3.2 中序遍历

在这里插入图片描述

根据图写出代码:

void MidOrderStack(BinaryNode<char>* root, std::vector<char>& vec)
{
	if (root == nullptr)
		return;
	std::stack< BinaryNode<char>*> st;
	st.push(root);
	BinaryNode<char>* cur = root->_left; //防止根被入栈两次
	//重新返回根节点访问右子树时,stack为空,cur不为空,
	//此时需要添加条件cur != nullptr判断,否则无法访问右子树
	while (cur != nullptr || !st.empty()) 
	{
		if (cur != nullptr)
		{
			st.push(cur);
			cur = cur->_left;
		}
		else  //cur == nullptr
		{
			cur = st.top();
			vec.push_back(cur->_val);
			st.pop();
			cur = cur->_right;
		}
	}
}

3.3 后序遍历

  二叉树后序遍历可以使用前序遍历的方式。遍历顺序:左——右——根,反过来是根——右——左,只要中序的代码交换入栈的左右子树顺序即可。最后记得反转输出。

void PostOrderStack(BinaryNode<char>* root, std::vector<char>& vec)
{
	if (root == nullptr)
		return;
	std::stack< BinaryNode<char>*> st;
	st.push(root);
	while (!st.empty())
	{
		BinaryNode<char>* tp = st.top();
		vec.push_back(tp->_val);
		st.pop();

		if (tp->_left != nullptr)   //这里交换了入栈的顺序
			st.push(tp->_left);
		if (tp->_right != nullptr)
			st.push(tp->_right);
	}
	reverse(vec.begin(), vec.end());
}

3.4 层序遍历

在这里插入图片描述

根据图写出代码:

void LayerOrder(BinaryNode<char>* root, std::vector<char>& vec)
{
	if (root == nullptr)
		return;
	std::queue<BinaryNode<char>*> que;
	que.push(root);
	while (!que.empty())
	{
		BinaryNode<char>* cur = que.front();
		vec.push_back(cur->_val);
		que.pop();

		if(cur->_left)
			que.push(cur->_left);
		if (cur->_right)
			que.push(cur->_right);
	}
}

  如果要将每一层分别写到一个数组里,类似于[[a], [b, c], [d, e, f]] ,则可以这样:

void LayerOrder(BinaryNode<char>* root, std::vector<std::vector<char>>& ret)
{
	if (root == nullptr)
		return;
	std::vector<char> vec;
	std::queue<BinaryNode<char>*> que;
	que.push(root);

	while (!que.empty())
	{
		int size = que.size(); //记录当前队列的大小,也即当前层的元素数量

		for (int i = 0; i < size; i++) //将当前层元素保存,并将当前层的所有元素的子节点添加到队列
		{
			BinaryNode<char>* cur = que.front();
			vec.push_back(cur->_val);
			que.pop();

			if (cur->_left)
				que.push(cur->_left);
			if (cur->_right)
				que.push(cur->_right);
		}
		ret.push_back(vec);
		vec.clear();
	}
}
//遍历可以这样写
//std::vector<std::vector<char>> ret;

//LayerOrder(Proot, ret);
//for (int i = 0; i < ret.size(); i++)
//{
//	std::cout << "[";
//	for (int j = 0; j < ret[i].size(); j++)
//		std::cout  << ret[i][j] << ",";
//	std::cout << "]" << " ,";
//}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值