二叉树的遍历应该说是非常常见的,用递归的话程序非常简单明了,而有的时候需要写非递归的遍历程序,这个时候就需要对此有一定的了解。
二叉树的遍历根据根节点和左右子节点遍历顺序的不同,可以分为三种:前序遍历,中序遍历,后序遍历。前序遍历就是先根节点->左节点->右节点。非递归前序遍历编写最简单,最难的是后续遍历。下面就来一一写出它们的程序,这里用的是Java。非递归遍历是一种DFS(深度优先搜素),从根节点开始一直遍历到叶子节点,然后在从根节点往另一个路径继续遍历。这里用到的数据结构是栈。Java里面用的是Stack类,它是数组实现,也可以用链表LinkedList来做栈。其实递归遍历也用到了栈,只不过是系统栈,没有显示的输出。
Node节点的定义:
public class Node<T>
{
private T element;
private Node<T> left;
private Node<T> right;
public Node(T element,Node<T> left,Node<T> right)
{
this.element=element;
this.left=left;
this.right=right;
}
}
递归二叉树遍历:
//前序遍历
public void printTree(Node<T> node)
{
if(node!=null)
{
System.out.print(node.element+" ");
printTree(node.left);
printTree(node.right);
}
}
//中序遍历
public void printTree(Node<T> node)
{
if(node!=null)
{
printTree(node.left);
System.out.print(node.element+" ");
printTree(node.right);
}
}
//后续遍历
public void printTree(Node<T> node)
{
if(node!=null)
{
printTree(node.left);
printTree(node.right);
System.out.print(node.element+" ");
}
}
非递归二叉树遍历:
//前序遍历 非递归实现 三种遍历中最简单的
public void preTrave(Node<T> root)
{
if(root==null)
return;
Stack<Node> st=new Stack<Node>();
st.push(root);
while(!st.isEmpty())
{
Node<T> n=st.pop();
System.out.print(n.element+" ");
if(n.right!=null)
st.push(n.right); //先入栈右节点,再入栈左节点
if(n.left!=null)
st.push(n.left);
}
}
//中序遍历 非递归实现 三种遍历中的
public void midTrave(Node<T> root)
{
if(root==null)
return;
Stack<Node> st=new Stack<Node>();
Node<T> no=root;
while(no!=null || !st.isEmpty())
{
while(no!=null)
{
st.push(no); //不停地插入左节点
no=no.left;
}
if(!st.isEmpty())
{
Node<T> n=st.pop();
System.out.print(n.element+" ");
no=n.right; //获取出栈节点的右节点
}
}
}
//后序遍历 非递归实现 三种遍历中是最难的,需要每个节点保存一个变量代表是否被读取过
public void postTrave(Node<T> root)
{
if(root==null)
return;
Map.Entry<Node,Boolean> me;//这里用到Map内部类,类似于C++的pair函数,否则要自己重写个类
Stack<Map.Entry<Node,Boolean>> st=new Stack<Map.Entry<Node,Boolean>>();
Node<T> no=root;
while(no!=null || !st.isEmpty())
{
while(no!=null)
{
me=new AbstractMap.SimpleEntry(no,false);
st.push(me); //不停地插入左节点
no=no.left;
}
if(!st.isEmpty())
{
Map.Entry<Node,Boolean> n=st.pop();
if(n.getValue()==false)
{
st.push(n);
n.setValue(true); //代表读过该节点一次
no=n.getKey().right;
}
else //代表出栈的节点右节点已经读取
{
System.out.print(n.getKey().element+" ");
no=null;
}
}
}
}
除了上面的三种遍历,二叉树还可以分层遍历。这就是BFS(广度优先搜索),先第一层,再第二层,以此类推。BFS用到的数据结构是队列,先入先出。程序如下,它和前序遍历类似,只不过把栈改成了队列,然后左右节点进入的顺序掉了一下。
程序如下:
//按层遍历 就是将前序遍历的栈改用为队列,左右节点入队顺序改变一下
public void cengTrave(Node<T> root)
{
if(root==null)
return;
ArrayDeque<Node> st=new ArrayDeque<Node>();
st.offer(root);
while(!st.isEmpty())
{
Node<T> n=st.pop();
System.out.print(n.element+" ");
if(n.left!=null)
st.offer(n.left); //先左节点入队列,再右节点
if(n.right!=null)
st.offer(n.right);
}
}