二叉树的非递归遍历

二叉树节点:

public class Node
{
	public int data = 0;
	public Node lchild = null;
	public Node rchild = null;
}

前序遍历

List<Node> PreorderTraverse(Node root)
{
	List<Node> nodes = new List<Node>();
	if (root == null)
	{
		return nodes;
	}

	Stack<Node> stack = new Stack<Node>();
	stack.Push(root);

	while (stack.Count > 0)
	{
		Node node = stack.Pop();
		nodes.Add(node);
		if (node.rchild != null)
		{
			stack.Push(node.rchild);
		}
		if (node.lchild != null)
		{
			stack.Push(node.lchild);
		}
	}
	return nodes;
}

前序遍历非常简单,先把根节点压入栈中,然后每从栈里取一个节点,就把它的两个子节点放入栈中,这样循环直到栈空即可。


中序遍历

List<Node> InorderTraverse(Node root)
{
	List<Node> nodes = new List<Node>();
	if (root == null)
	{
		return nodes;
	}

	Node node = root;
	Stack<Node> stack = new Stack<Node>();

	while (node != null || stack.Count > 0)
	{
		if (node != null) // 状况1
		{
			stack.Push(node);
			node = node.lchild; // node变为状况1或3的节点
		}
		else // 状况3
		{
			node = stack.Pop(); // node变为状况2的节点
			nodes.Add(node);
			node = node1.rchild; // node变为状况1或3的节点
		}
	}
	return nodes;
}

中序遍历稍微麻烦点,因为我们每次拿到手的节点有三种状况:

1. 它是一个尚未遍历的子树的根节点,其左子树还没处理,所以我们要把它入栈,然后处理它的左子节点,继续状况1或3。

2. 它是左子树遍历完成后的从栈取出的父节点,它的左子树已经处理完毕,只要把它输出然后就可以忽略它了,然后处理它的右子节点,回到状况1或3。

3. 它是空的,直接从栈中取新的节点,回到状况2。


后序遍历

List<Node> PostorderTraverse(Node root)
{
	List<Node> nodes = new List<Node>();
	if (root == null)
	{
		return nodes;
	}

	Stack<Node> stack = new Stack<Node>();
	stack.Push(root);
	Node pnode = null;

	while (stack.Count > 0)
	{
		Node node = stack.Peek();
		// 如果pnode不是node的左/右子节点,表明node节点是以左/右子节点身份入栈的,那么以它为根的子树尚未遍历,需要把它的左右子节点入栈
		if (pnode == null || (pnode != node.lchild && pnode != node.rchild))
		{
			if (node.rchild != null)
			{
				stack.Push(node.rchild);
			}
			if (node.lchild != null)
			{
				stack.Push(node.lchild);
			}
		}
		// 如果它还是栈顶(没有子节点入栈),那么它一定是没有子节点或者是处理完子节点后退回到它
		if (stack.Peek() == node)
		{
			nodes.Add(node);
			stack.Pop();
			pnode = node;
		}
	}
	return nodes;
}

    后续遍历是最复杂的,主要是从栈中取出节点node的时候,node有多种情况(前序和中序里的栈顶节点都是直接输出并抛弃,省心)

    先看每个节点入栈时的状况:

1. 它是以子树的根节点的身份入栈的,一定同时还会把它的右子节点(状况2)和左子节点(状况3)入栈并压在它上面,当轮到它出栈时,它的左右子节点一定已先出栈并处理过了,此时我们直接输出它,代表以它为根的子树已经全部处理完毕

2. 它是以一个右子节点身份入栈的,那么轮到它出栈的时候,它是一颗尚未处理的子树的根节点,所以需要重新入栈进入状况1

3. 它是以一个左子节点身份入栈的,处理方法同2

    问题是当我们如何才能得知从栈顶弹出的node是状况1还是状况23下入栈的呢?这得看我们最近一次输出的节点pnode跟它的关系了:

1 如果pnode是node的子节点,则node一定是以状况1入栈的,如果是状况2/3,它的子节点都没入过栈,更不可能输出过。所以按照状态1直接输出node就好了

2 如果pnode跟node无关,则按状况2/3处理

3 如果pnode跟node无关,且node本身是叶节点,我们也把按照状态1直接输出它



中序遍历
中序遍历
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值