二叉树的遍历
包含四种遍历方式:
前序遍历
中序遍历
后序遍历
层次遍历
一.前序遍历
前序遍历按照“根结点-左孩子-右孩子”的顺序进行访问。
1.递归实现
// 前序遍历:首先访问根结点,然后遍历其左子树,最后遍历其右子树。
private void PreOrder(TreeNode<T> node)
{
if (node == null)
{
return;
}
//打印节点数据
Console.WriteLine(node.Data);
PreOrder(node.LeftChild);
PreOrder(node.RightChild);
}
2.非递归实现
实现思路:根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问完左子树时,再访问它的右子树。因此其处理过程如下:
对于任一结点P:
1)访问结点P,并将结点P入栈;
2)若结点P的右孩子不为空,把右孩子结点入栈;
3)若结点P的左孩子不为空,则将左孩子结点置为当前的结点P;
4)当遍历完左子树,Pop出右孩子结点,继续上面的遍历;
4)直到P为null并且栈为空,则遍历结束。
// 对于树的遍历若采用非递归的方法,就要采用栈去模拟实现。
// 前序遍历
private void PreOrder1(TreeNode<T> node)
{
Stack<TreeNode<T>> s = new Stack<TreeNode<T>>();
TreeNode<T> curNode = node;
s.Push(node);
while (s.Count > 0)
{
Console.WriteLine(curNode.Data);
if (curNode.RightChild != null)
{
s.Push(curNode.RightChild);
}
if (curNode.LeftChild != null)
{
curNode = curNode.LeftChild;
}
else
{
//左子树访问完了,访问右子树
curNode = s.Pop();
}
}
}
二.中序遍历
中序遍历按照“左孩子-根结点-右孩子”的顺序进行访问。
1.递归实现
// 中序遍历:首先遍历其左子树,然后访问根结点,最后遍历其右子树。
private void InOrder(TreeNode<T> node)
{
if (node == null)
{
return;
}
InOrder(node.LeftChild);
//打印节点数据
Console.WriteLine(node.Data);
InOrder(node.RightChild);
}
2.非递归实现
实现思路:根据中序遍历的顺序,对于任一结点,优先遍历其左孩子,而左孩子结点不为空,然后继续遍历其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。因此其处理过程如下:
对于任一结点P,
1)访问结点P,并将结点P入栈;
2)优先遍历其左孩子,而左孩子结点不为空,然后继续遍历其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,出栈左孩子节点,然后遍历右孩子节点,继续出栈,直至出栈根节点,继续遍历右子树;
3)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将栈顶结点的右孩子置为当前的P;
// 对于树的遍历若采用非递归的方法,就要采用栈去模拟实现。
// 中序遍历
private void InOrder1(TreeNode<T> node)
{
Stack<TreeNode<T>> s = new Stack<TreeNode<T>>();
TreeNode<T> curNode = node;
while (curNode != null || s.Count > 0)
{
while (curNode != null)
{
s.Push(curNode);
curNode = curNode.LeftChild;
}
if (s.Count > 0)
{
curNode = s.Pop();
Console.WriteLine(curNode.Data);
curNode = curNode.RightChild;
}
}
}
三.后序遍历
后序遍历按照“左孩子-右孩子-根结点”的顺序进行访问。
1.递归实现
// 后序遍历:首先遍历其左子树,然后遍历其右子树,最后访问根结点。
private void PostOrder(TreeNode<T> node)
{
if (node == null)
{
return;
}
PostOrder(node.LeftChild);
PostOrder(node.RightChild);
//打印节点数据
Console.WriteLine(node.Data);
}
2.非递归实现
后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。
实现思路:
1)先把根节点入栈
2)取得栈顶元素,如果当前结点没有孩子节点或者孩子节点都已经被访问,那么出栈,访问其数据
3)否则,如果右孩子节点不为空,入栈。如果左孩子不为空,入栈。要保证入栈顺序,先右孩子再左孩子节点。
4)只要栈中还有元素,就循环执行2)3)
注意:要保证根结点在左孩子和右孩子访问之后才能访问
// 后序遍历
private void PostOrder1(TreeNode<T> node)
{
Stack<TreeNode<T>> s = new Stack<TreeNode<T>>();
TreeNode<T> curNode = null;
TreeNode<T> preNode = null;
s.Push(node);
while (s.Count > 0)
{
curNode = s.Peek();
if ((curNode.LeftChild == null && curNode.RightChild == null) ||
(preNode != null && (preNode == curNode.LeftChild || preNode == curNode.RightChild)))
{
//如果当前结点没有孩子结点或者孩子节点都已被访问过
Console.WriteLine(curNode.Data);
s.Pop();
preNode = curNode;
}
else
{
if (curNode.RightChild != null)
{
s.Push(curNode.RightChild);
}
if (curNode.LeftChild != null)
{
s.Push(curNode.LeftChild);
}
}
}
}
四、层次遍历
从树的第一层开始,从上到下逐层遍历,在同一层中,从左到右对结点逐个访问输出,采用队列的方式进行层次遍历。
// 层次遍历
private void HierarchyOrder(TreeNode<T> node)
{
Queue<TreeNode<T>> s = new Queue<TreeNode<T>>();
s.Enqueue(node);
while(s.Count > 0)
{
TreeNode<T> curNode = s.Dequeue();
Console.WriteLine(curNode.Data);
if(curNode.LeftChild != null)
{
s.Enqueue(curNode.LeftChild);
}
if(curNode.RightChild != null)
{
s.Enqueue(curNode.RightChild);
}
}
}