我们都知道二叉树的遍历方式常用的两种方式:
至于广度优先我前面:http://t.csdn.cn/kM5fR 这篇博客的分层打印其实就是一个广度优先的实现
本篇将介绍java中二叉树的深度优先遍历的实现方式。
深度优先又可以分为:
这里的 “前、后、中” 是指父节点的输出顺序。
先从简单的递归实现方式来实现这三种遍历吧
前序:
/**
* 前序遍历
* @param node 二叉树节点(根节点)
*/
public static void preOrder(final TreeNode node)
{
if (node == null)//如果节点为空,终止递,开始归回溯
{
return;
}
System.out.print(node.getVal() + "\t");//输出节点值
preOrder(node.getLeft());//递归遍历左子树
preOrder(node.getRight());//递归遍历右子树
}
前序就是先输出,再遍历左子树,再遍历右子树
中序:
/**
* 中序遍历
* @param node 二叉树节点(根节点)
*/
public static void inOrder(final TreeNode node)
{
if (node == null)//如果节点为空,终止递,开始归回溯
{
return;
}
inOrder(node.getLeft());//递归遍历左子树
System.out.print(node.getVal() + "\t");//输出节点值
inOrder(node.getRight());//递归遍历右子树
}
中序就先遍历左子树,再在归的过程时输出,再遍历右子树
后序:
/**
* 后序遍历
* @param node 二叉树节点(根节点)
*/
public static void pastOrder(final TreeNode node)
{
if (node == null)//如果节点为空,终止递,开始归回溯
{
return;
}
pastOrder(node.getLeft());//递归遍历左子树
pastOrder(node.getRight());//递归遍历右子树
System.out.print(node.getVal() + "\t");//输出节点值
}
同理,后序就先遍历完左右子树,再在归的时候输出
用递归代码写是好写,但是用的时候就不一定好用了,因为递归很容易有栈溢出的风险
那就用迭代吧:
前序:
/**
* 前序遍历
* @param node 二叉树节点(根节点)
*/
public static void preOrder2(final TreeNode node)
{
LinkedListStack stack = new LinkedListStack<>();
TreeNode curr = node;
while (curr != null || !stack.isEmpty())
{
if (curr != null)
{
System.out.print(curr.getVal() + "\t");
stack.push(curr);//压入栈,记录回路
curr = curr.getLeft();//探索左子树
}
else
{
TreeNode pop = stack.pop();
curr = pop.getRight();//探索右子树
}
}
}
前序就先输出父节点,然后把父节点压入栈,再去探索左节点,什么时候没有节点了,探索右节点
当然,这里的LinkedListStack类是我自己写的一个实现栈的类,前面 http://t.csdn.cn/9e40A 介绍栈的时候有介绍,你也可以用LinkedList来代替。
中序:
/**
* 中序遍历
* @param node 二叉树节点(根节点)
*/
public static void inOrder2(final TreeNode node)
{
LinkedListStack stack = new LinkedListStack<>();
TreeNode curr = node;
while (curr != null || !stack.isEmpty())
{
if (curr != null)
{
stack.push(curr);//压入栈,记录回路
curr = curr.getLeft();
}
else
{
TreeNode pop = stack.pop();
System.out.print(pop.getVal() + "\t");
curr = pop.getRight();
}
}
}
中序就是先遍历完了左子树,没有节点了,再从栈弹出左子树,父节点;要是父节点的右子树不为空,就继续探索右子树
后序:
/**
* 后序遍历
* @param node 二叉树节点(根节点)
*/
public static void pastOrder2(final TreeNode node)
{
LinkedListStack stack = new LinkedListStack<>();//创建栈
TreeNode curr = node;//当前节点
TreeNode pop = null;//记录上一个出栈的节点
while (curr != null || !stack.isEmpty())
{
if (curr != null)
{
stack.push(curr);//压入栈,记录回路
curr = curr.getLeft();//待处理左子树
}
else
{
TreeNode peek = stack.peek();//获取栈顶元素
if (peek.getRight() == null || peek.getRight() == pop)//如果栈顶元素的右子树为空或者右子树已经处理完毕,就把栈顶元素出栈并输出
{
pop = stack.pop();
System.out.print(pop.getVal() + "\t");
}
else
{
curr = peek.getRight();//待处理右子树
}
}
}
}
这个后序遍历稍微麻烦一点,因为探索完右子树还要找回去的路,所以不能直接把父节点在回去的过程中给弹出了,得先查看它还有没有右子树,如果没有,那直接弹出没关系,如果有,那就先探索完右子树再回来看一下,如果当前节点的右子树已经确认被弹出了,再弹出当前节点;变量 “pop” 专门用来记录刚刚弹出的元素。
最后我把这三种非递归的方法整合成一个方法:
public static void Order(final TreeNode node)
{
LinkedListStack stack = new LinkedListStack<>();
TreeNode curr = node;
TreeNode pop = null;
while (curr != null || !stack.isEmpty())
{
if (curr != null)
{
stack.push(curr);//压入栈,记录回路
colorPrintln("前"+curr.getVal(),31);
curr = curr.getLeft();//待处理左子树
}
else
{
TreeNode peek = stack.peek();
if (peek.getRight() == null)//没有右子树
{
colorPrintln("中"+peek.getVal(),33);
pop = stack.pop();
colorPrintln("后"+pop.getVal(),34);
}
else if ( peek.getRight() == pop)//右子树处理完毕
{
pop = stack.pop();
colorPrintln("后"+pop.getVal(),34);
}
else
{
colorPrintln("中"+peek.getVal(),33);
curr = peek.getRight();//待处理右子树
}
}
}
}
/**
* 打印彩色字体
* @param content 文本内容
* @param color 颜色
*/
public static void colorPrintln(String content,int color)
{
System.out.printf("\u001B[%dm%s\u001B[0m%n",color,content);
}
需要哪一种就保留哪一种的输出就行,把其他遍历方式对应的输出删掉就行。