二叉树的遍历-递归和非递归实现

二叉树的遍历

二叉树遍历就是遍历每一个节点,比较常用的就是前中后序遍历的方式,本文介绍递归遍历和非递归遍历两种方式。

前序遍历:就是先输出根节点,再左节点再右节点。

中序遍历:先左节点,再中节点再右节点,如果是有序的二叉树,这种方式输出就是有序的。

后序遍历:先左节点,再右节点,最后根节点。

所谓的前中后就是什么时候输出或者说操作根节点。

递归遍历

递归遍历的思路是这样的拿到一个节点,判断节点是否为空,如果是空的话就返回,这就是递归的出口,否则就左右递归即可。

代码如下,比较简单:

	struct Node {
	
		KEY_TYPE key;
	
		struct Node* l;
		struct Node* r;
	};
	
	void frontShowRecursion(Node* root)
	{
		if (NULL == root) return;
		
		cout << root->key << " ";
		frontShowRecursion(root->l);
		frontShowRecursion(root->r);
	
	}
	void midShowRecursion(Node* root)
	{
		if (NULL == root) return;
	
		midShowRecursion(root->l);
		cout << root->key << " ";
		midShowRecursion(root->r);
	
	}
	void backShowRecursion(Node* root)
	{
		if (NULL == root) return;
	
		
		backShowRecursion(root->l);
		backShowRecursion(root->r);
		cout << root->key << " ";
	
	}

非递归遍历

非递归遍历就要用栈来实现了,栈在什么时候比较好用呢?

**题外话:**栈这种数据结构还是很有用的,在需要先保存某种状态的时候,然后去处理其它情况之后再返回这种状态继续操作的时候就可以用栈,比如函数执行的过程,就是,先把后续指令入栈,然后再去操作函数指令入栈,操作完了函数部分出栈,然后继续操作后续指令,也就是说栈是保存一个状态的。

扯远了,不过想想其实,理解了栈,才能在这里用起来,比如只有三个节点的树,中序遍历,先拿到根节点,由于是中序遍历,所以要先去操作左节点,这时候应该怎么处理根节点,就可以使用栈,把根节点入栈,然后去操作左边的节点,然后就可以从栈中拿出根节点并且操作了,然后再去操作右节点,这就是栈的思想。也就是说,栈是为了保存一下状态,操作完其它节点的时候再返回继续操作这个节点。

代码:

void frontShowWithoutRecursion(Node* root)
{
	if (NULL == root) return;
	Node* node = root;
	//先打印出根节点,然后根节点入栈,因为需要遍历右子树,所以把根节点存起来

	stack<Node*> s;
    //栈非空,或者节点非空
	while (!s.empty() || node)
	{
		if (node)
		{//先输出当前节点,然后入栈,然后访问继续操作左节点
			cout << node->key << " "<< endl;
			s.push(node);
			node = node->l;
		}
		else
		{//当前节点为空,那就从栈里保存的节点右节点开始操作
			node = s.top()->r;
			s.pop();
		}
	}
}

void midShowWithoutRecursion(Node* root)
{
	if (NULL == root) return;

	Node* node = root;

	stack<Node*> s;
	while (!s.empty() || node)
	{
		if (node)
		{//节点非空就入栈,然后操作左边
			s.push(node);
			node = node->l;
		}
		else
		{//节点为空,就代表没有左节点了,那么就输出当前节点,并出栈,然后操作右子树
			node = s.top();
			cout << node->key << " ";
			s.pop();
			node = node->r;
		}
	}

}

//先遍历左边,再遍历右子树,最后才根节点
void backShowWithoutRecursion(Node* root)
{
	if (NULL == root) return;

	stack<Node*> s;
	Node* cur = root;
	Node* pre = NULL;//记录上一次访问的节点,只有上一次访问的是空节点或者是右边,才能访问根节点
	
	while (cur)
	{
		s.push(cur);
		cur = cur->l;
	}
	while (!s.empty())
	{
		cur = s.top();
		s.pop();

		if (cur->r == NULL || pre == cur->r)
		{
			cout << cur->key << " ";
			pre = cur;//修改之前访问的节点为当前节点
		}
		else
		{
			s.push(cur);
			cur = cur->r;
			while (cur)
			{
				s.push(cur);
				cur = cur->l;
			}
		}
	}
}

总结

  • 树的遍历都有点递归思想的体现,以根节点为基准操作,根据先后操作根节点分为前中后序遍历,在遍历左子树的时候就是把左节点当成新的根,右子树同理。
  • 栈在非递归中就是为了保存节点状态,以便能够返回,因为不急着操作某某个节点的时候,就应该把它入栈,或者需要这个节点来操作它的子节点的时候也需要入栈(后续遍历)
  • 总之树的操作就是递归的思想,递归去操作每一个节点,虽然是非递归方法,但是思想还是有递归的味道,因为递归都是可以写成循环的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值