定义
首先我们要知道前序遍历、中序遍历、后序遍历三种遍历方式的定义
- 前序遍历是根->左->右
- 中序遍历是左->根->右
- 后序遍历是左->右->根
根据定义,我们很容易发现规律,前中后分别指的是根的访问顺序,比如前序,就是根在最前面,最先访问根节点再左然后再右,中序就是根节点在中间访问,后序就是根节点最后访问,但是无论哪种遍历方式,左子节点永远比右子节点先访问
假设有下面一颗二叉树
那么:
前序遍历结果为:8,6,4,3,5,7,10,9,11
中序遍历结果为:3,4,5,6,7,8,9,10,11
后序遍历结果为:3,5,4,7,6,9,11,10,8
可以发现在中序遍历搜索二叉树的时候,结果是有序的,所以一般输出搜索二叉树的所有节点的时候会采用中序遍历的方式
前序遍历描述:
对照着上面的图来讲解
从根节点8出发,访问左子节点6,然后左子节点6的左子节点4,将路过的节点依次输出,直到到达叶子节点3,然后再访问叶子节点的父节点4的右子节点5,如果5有左子节点那么继续访问5的左子节点,当4的右子节点遍历完成了之后,在向上找4的父节点6的右子节点
总结:有左就先左,直到最底层,左子树全部遍历完成了,再找父节点的右子树
画个图好理解一点,箭头就是前序遍历的访问顺序
中序遍历描述:
先遍历左子树,然后遍历自己,最后遍历右子树,左子树没遍历完绝对不遍历自己,自己没遍历过的话,绝对不遍历右子树
子树的遍历也满足前面的逻辑
按照这个逻辑一定会先递归到最左边的叶子节点,然后最左叶子节点遍历完成,然后遍历父节点,父节点遍历完了,在遍历右边的
总结:有左就左,然后自己,然后右边
后序遍历描述:
先遍历左子树,然后再遍历右子树,就是如果有子节点没遍历过或者子树没有遍历完,那么绝对不遍历自己,遍历子树的时候也满足上面的规则
根据这个规则,那么一定是先怼到最左边的节点,可以肯定的是最左边的节点肯定是没有左子节点了,那么有下面两种情况
假设1:最左边的节点有右子节点,根据子节点优先,有一个子节点的先输出子节点
假设2:最左边的节点没有任何子节点,输出自己
两个假设的情况整完了,开始找父节点的右子树,如果父节点有右子树,那么将右子树也按照左右中的这个逻辑来遍历
定义Node节点和树
就简单的定义一下
public class Tree{
static class Node<T>{
private Node left;
private Node right;
private T data;
public Node(T t){
this.data = t;
}
}
private static Node head;
}
前序遍历代码实现
递归
private static List<Object> preOrder(Node node){//先序递归遍历
List<Object> list = new LinkedList<>();
if(node!=null){
list.add(node.data);//先自己
list.addAll(preOrder(node.left));//再左边
list.addAll(preOrder(node.right));//再右边
}
return list;
}
public static List<Object> preOrder(){//调用先序递归遍历
return preOrder(head);
}
非递归,使用栈
其实递归的原理就是栈,先进先出,所以用栈也可以实现非递归的方式遍历
private static List<Object> sqlPreOrder(){//先序非递归
List<Object> list = new LinkedList<>();
Stack<Node> stack = new Stack<>();
Node p = head;
while (p != null || !stack.isEmpty()){
if(p != null){//如果当前节点不为空
list.add(p.data);//先输出自己
stack.push(p);//将自己压入栈
p = p.left;//访问左节点
}else {//如果当前节点为空
p = stack.pop();//那么弹出栈顶元素
p = p.right;//访问栈顶元素的右节点
}
}
return list;
}
public static List<Object> sqlPreOrder(){//调用先序非递归遍历
return sqlPreOrder(head);
}
中序遍历代码实现
递归
private static List<Object> inOrder(Node node){//中序递归遍历
List<Object> list = new LinkedList<>();
if(node!=null){
list.addAll(inOrder(node.left));
list.add(node.data);
list.addAll(inOrder(node.right));
}
return list;
}
非递归,同样用栈
先序遍历的时候是先访问自己再入栈,那么中序就是先入栈,再访问左节点,再入栈,再访问左节点,直到没有左节点,那么弹出栈顶,其实此时的栈顶就是自己,遍历自己,然后遍历右节点,右节点重复操作,直到右节点为空,或者右节点都遍历过,那么再弹就是自己的父节点了
public static List<Object> sqlInOrder(){//中序非递归
List<Object> list = new LinkedList<>();
Stack<Node> stack = new Stack<>();
Node p = head;
while (p != null || !stack.isEmpty()){
if(p != null){
stack.push(p);
p = p.left;
}else {
p = stack.pop();
list.add(p.data);
p = p.right;
}
}
return list;
}
后序遍历代码实现
递归
private static List<Object> postOrder(Node node){//后序递归遍历
List<Object> list = new LinkedList<>();
if(node!=null){
list.addAll(postOrder(node.left));
list.addAll(postOrder(node.right));
list.add(node.data);
}
return list;
}
public static List<Object> postOrder(){//调用后序递归遍历
return postOrder(head);
}
非递归
后序的非递归实现有点复杂,下次再写