一.摘要
树的遍历有前序遍历,中序遍历,后续遍历三种方式,每种方式又有递归和非递归两种实现方法,以下是各种遍历方式的Java实现,这三种遍历方式的实现有下相同的点,递归的方法思路是比较清晰的,而非递归会稍微复杂一些,都需要用到栈的数据结构。
先定义树节点的结构
public class TreeNode()
{
TreeNode left;//左孩子
TreeNode right;//右孩子
int val;//节点值
//构造函数
public TreeNode(int val)
{
this.val = val;
}
}
二.前序遍历
顾名思义,遍历的顺序是先遍历根节点,如果有左孩子再遍历左节点,如果有右孩子再遍历右节点
2.1 前序遍历递归实现
代码:
public void preOrderTraversal(TreeNode root)
{
//先处理根节点
if(root == null)
{
return ;
}else
{
System.out.println(root.val);
}
//再处理左节点
preOrder(root.left);
//最后处理右节点
preOrder(root.right);
}
2.2前序遍历非递归实现
用栈来实现,根节点不空则入栈,栈不空时做以下循环:
step1:当前节点出栈,打印它(访问它,遍历它),
step2:如果当前节点有右孩子,则右孩子入栈
step3:如果当前节点有左孩子,则左孩子入栈
Tips:
为什么右孩子先入栈左孩子后入栈呢?因为栈是先进后出的,右孩子先进,左孩子后进,出栈的时候就会反过来,左孩子先出,右孩子后出,这就符合前序遍历的规律了
代码:
public void preOrderTraversal(TreeNode root)
{
if(root == null)
{
return;
}
//根节点不空时:
Stack<TreeNode> s = new Stack<TreeNode>();//新建一个栈
s.push(root);
//栈不空时做以下循环
while(!s.isEmpty())
{ //出栈栈定元素
TreeNode current = s.pop();
System.out.println(current.val);
//右孩子不空则进栈
if(current.right!=null)
{
s.push(current.right);
}
//左孩子不空则进栈
if(current.left!=null)
{
s.push(current.left);
}
}
}
三.中序遍历
遍历的顺序是如果有左孩子先遍历左节点,然后遍历根节点,如果有右孩子再遍历右节点。
3.1 中序遍历递归实现
代码:
public void inOrderTraversal(TreeNode root)
{
//根节点若不否为空
if(root != null)
{
/先遍历左节点
preOrder(root.left);
//然后遍历根节点
System.out.println(root.val);
//最后遍历右节点
preOrder(root.right);
}
}
3.2中序遍历非递归实现
步骤:
1)申请一个新的栈
2)初始另cur=头节点,将cur压入,然后一直让cur=cur.left
3)当cur为空时,从栈中弹出节点它记为node并打印它的值,并让cur=node.right,重复步骤2)
4)当stack为空且cur也为空时,整个过程结束
代码:
public void inOrderTraversal(TreeNode root)
{
if(root == null)
{
return;
}
//根节点不空时:
Stack<TreeNode> s = new Stack<TreeNode>();//新建一个栈
TreeNode cur = root;
//栈不空时做以下循环
while(!s.isEmpty()||cur!=null)
{ if(cur!=null)
{
s.push(cur);
cur = cur.left;
}else{
TreeNode node = s.pop();
System.out.println(node.val);
cur = node.right;
}
}
}
四.后序遍历
遍历的顺序是如果有左孩子先遍历左节点,如果有右孩子再遍历右节点,最后遍历根节点。
4.1 后序遍历递归实现
代码:
public void postOrderTraversal(TreeNode root)
{
//根节点若不否为空
if(root != null)
{
//先遍历左节点
preOrder(root.left);
//然后遍历右节点
preOrder(root.right);
//最后遍历根节点
System.out.println(root.val);
}
}
4.2后序遍历非递归实现
用s1,s2两个栈实现,步骤是:
step1: 将头节点压入s1,
step2: 从s1中弹出节点记作cur,然后把cur的左孩子压入s1,右孩子压入s1,
step3: 在这个过程中每弹出一个节点都放入s2中
不断重复步2,3直到s1为空,过程停止,从s2中依次弹出节点并打印,打印的顺序就是后续遍历的顺序
代码:
public void postOrderTraversal(TreeNode root)
{
if(root == null)
{
return;
}
//根节点不空时:
//新建2个栈
Stack<TreeNode> s1 = new Stack<TreeNode>();
Stack<TreeNode> s2 = new Stack<TreeNode>();
s1.push(root);
//栈不空时做以下循环
while(!s1.isEmpty())
{
TreeNode cur = s1.pop();
//弹出的节点放入s2
s2.push(cur);
//左孩子不空则进栈s1
if(cur.left!=null)
{
s1.push(cur.left);
}
//右孩子不空则进栈s1
if(cur.right!=null)
{
s1.push(cur.right);
}
}
//最后打印s2依次出栈的结果
while(!s2.isEmpty())
{
TreeNode result = s1.pop();
System.out.println(result .val);
}
这个方法真神奇,一个节点进了s2就可以带着他的的两个亲孩子进s1,控制好顺序就是后续遍历了,根先出s1,先进s2,可以保证根在后面,而每次只进左右孩子,保证了一个局部性,不会牵扯太多的元素,但每次就处理亲近的就刚刚好,太妙了。
五.给自己的问题(后续可能会更新)
能不能用两个栈来做中序遍历?