二叉树的建立及遍历(3000字详解)

二叉树的建立

二叉树我这里采用了递归的方法建立,从根节点出发,先递归左子树,再递归右子树。空节点用'#'表示,建立的顺序如下图。注意在输入节点的时候也要按照建立的顺序输入,图中节点的顺序:ABC##D##E##

给出代码如下:

//递归生成二叉树
void Createtree(BTree& T)
{
	char aa;
	cin >> aa;
	if (aa == '#')
	{
		T = NULL;
	}
	else {
		T = new TreeNode;
		T->val = aa; 					//生成根节点
		Createtree(T->left); 			//递归创建左子树
		Createtree(T->right); 			//递归创建右子树
	}
}

二叉树的遍历

        先序遍历

        先序遍历用递归很好实现,先访问根节点,然后一直递归访问左子树节点,再访问右子树节点。代码如下:

void Front(BTree T)
{
	vector<char> res;
	if (T != nullptr)
	{
		res.push_back(T->val);    
		visit(res);               //访问根节点
		Front(T->left);           //访问左子树
		Front(T->right);          //访问右子树
	}
}

如果不用递归呢?可以采用栈来模拟递归。先序遍历的顺序:根左右,所以可以先把根节点放入栈中,弹出根节点后,再分别把左右节点放入,那么此时问题来了,是先放左节点还是右节点呢? 我们知道栈是一种先入后出的数据结构,如果先入左节点再入右节点,那么在出栈的时候则是:根右左,不满足先序遍历的顺序,所以在入栈的时候先入右节点再入左节点,出来的顺序则是:根左右。然后一直按照根右左入栈下去,一直找到左子树最左边节点。

先序遍历非递归
void Front(BTree T)
{
	if (T == NULL) return ;    //传入节点不合法
	stack<TreeNode*> sk;
	vector<char> res;

	sk.push(T);                //根节点入栈
	while (!sk.empty())
	{
		TreeNode* node = sk.top();     
		sk.pop();
		res.push_back(node->val);        //访问根节点
		
		if (node->right) sk.push(node->right);    //根右节点入栈
		if (node->left)  sk.push(node->left);     //根左节点入栈
	}
	visit(res);
}

        中序遍历

已经了解了先序遍历的思想,那么中序遍历又是怎么样子的呢?中序遍历顺序:左根右 ,也就是说得先找到最左边的一个节点处理他。

如果是这种情况:

一直找到最左边是C,把C入栈,再弹出该节点存入数组中,然后此时栈最上面一定是B(因为先一直找左节点),再把B节点弹出,存入数组,最后把D放入栈中,类似的再弹出放入数组。

如果是这种情况:

       

此时没有右节点,前两步与第一种情况相同,把B弹出后,继续往上找。

前两种都是最左边节点无孩子的情况。总的来说最左边无孩子,则往上找节点,再找右节点。

 还有一种情况,最左边节点有孩子的情况

 B为最左边节点,B入栈然后弹出,然后找他的右孩子D,放入栈中然后弹出。再向上找B的根节点,再重复以上三种情况。

总的来说:最左边有孩子,先找该节点的右孩子,再往上找。

总结三种情况:在弹出最左边一个节点后,则可以用一个判断语句判断:如果当前节点无右孩子节点,则向上找,如果有右孩子节点,则把右节点放入栈中。

给出代码:

//中序遍历非递归
void Inorder(BTree T)
{
	vector<char> vv;
	stack<BTree> sk;
	TreeNode* cur = T;

	while (cur ||!sk.empty())
	{
		if (cur != nullptr)   
		{
			sk.push(cur);
			cur = cur->left;    //最左边找节点
		}
		else
		{
			cur = sk.top();    //处理最左边节点
			sk.pop();
			
			vv.push_back(cur->val);
			cur = cur->right;    //处理右孩子节点
		}                        //if else 是如果右孩子不存在,则向上找
                                 //存在则把右孩子放入栈中

	}
	visit(vv);
}

递归则很好写出:

void Inorder(BTree T)
{
	vector<char> res;
	if (T != nullptr)
	{
		Inorder(T->left);        //一直左找
		res.push_back(T->val);   //处理左节点 
		visit(res);
		Inorder(T->right);        //在处理右节点
		
	}
}

        后序遍历

  后续遍历,前面已经用栈实现了先序遍历,如果我们把入栈的顺序改一下,先入根节点,然后弹出,再入左节点,再入右节点的话,最后得到的顺序是:根右左。很有意思,如果把它倒过来,则是左右根,就是后序遍历,所以后序遍历只需要改变左右节点的入栈顺序,再让输出反转则得到了后序遍历结果。(注下面动图没有让输出反转,只是模拟了改变左右节点入栈的顺序)

给出递归和非递归代码:

后序遍历非递归
void Back(BTree T)
{
	if (T == NULL) return ;
	stack<TreeNode*> sk;
	vector<char> res;

	sk.push(T);
	while (!sk.empty())
	{
		TreeNode* node = sk.top();
		sk.pop();
		res.push_back(node->val);

		if (node->left)  sk.push(node->left);    //改变顺序
		if (node->right) sk.push(node->right);

	}
	reverse(res.begin(), res.end());        //输出反转
	visit(res);
}

        层序遍历

层序遍历用一个队列模拟,先存入根节点,弹出;然后放入根节点的左孩子与右孩子,在弹出根节点的左孩子,同时存入左孩子的左孩子与右孩子,再在队首弹出根节点的左孩子,再在队尾入根节点右孩子的左孩子与右孩子。即出一个节点,就要存入该节点的左孩子与右孩子

//层序遍历
void level(TreeNode* T)
{
	vector<char> res;
	queue<TreeNode*> que;

	if (T != nullptr) que.push(T);    //存入根节点
	while (!que.empty())
	{
		int size = que.size();
		for (int i = 0; i < size; ++i)
		{
			TreeNode* node = que.front();
			que.pop();
			res.push_back(node->val);    //根节点的值存在数组

			if (node->left) que.push(node->left);    //队列存入根节点左孩子
			if (node->right) que.push(node->right);  //队列存入根节点右孩子
		}
	}
	visit(res);
}

完整代码 

二叉树数据结构

struct TreeNode
{
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode() : val(0), left(nullptr), right(nullptr) {}
};
using BTree = TreeNode*;

 二叉树访问函数

void visit(vector<char> nums)
{
	for (int i=0;i<nums.size();++i)
		cout << nums[i] << " ";
}

 主函数

int main()
{
	TreeNode* T;
	cout << "建立二叉树:" << endl;
	Createtree(T);
	cout << "前序遍历:" << endl;
	Front(T); cout << endl;
	cout << "中序遍历:" << endl;
	Inorder(T); cout << endl;
	cout << "后序遍历:" << endl;
	Back(T); cout << endl;
	cout << "层序遍历" << endl;
	level(T); cout << endl;

	
//ABC##D##E##
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值