概念理解
二叉树所谓的前序遍历、中序遍历、后序遍历中,前中后的意思,其实指的是根节点相比较于他的左右孩子节点,何时打印的意思。比如前序遍历的意思,指的就是根节点相较于他的左右孩子先打印,然后再打印其左右孩子,这就是根节点优先,所以叫先序遍历。中序遍历,根节点在打印完左孩子后,在中间打印,所以叫做中序遍历。后序遍历,是先打印完左右孩子,再打印中间根节点,因此是后续遍历。
递归方式的前中后序遍历
递归方式是最容易实现二叉树的前中后序遍历的,因为递归方式的代码,能非常直观的体现出二叉树三种遍历方式的特点。
前序遍历
代码中可以清晰看到,先打印根节点,再递归其左孩子,然后是右孩子。打印根节点在最前面。
//递归前序遍历
public static void preOrderRecur(TreeNode head){
if(head==null){
return;
}
System.out.println(head.value);
preOrderRecur(head.left);
preOrderRecur(head.right);
}
中序遍历
代码中可以清晰看到,先递归左孩子,再打印根节点,然后是右孩子。打印根节点在中间。
//递归中序遍历
public static void inOrderRecur(TreeNode head){
if(head==null){
return;
}
inOrderRecur(head.left);
System.out.println(head.value);
inOrderRecur(head.right);
}
后序遍历
先递归左右孩子,再打印根节点,打印根节点在最后。
//递归后序遍历
public static void afterOrderRecur(TreeNode head){
if(head==null){
return;
}
afterOrderRecur(head.left);
afterOrderRecur(head.right);
System.out.println(head.value);
}
非递归实现前中后序遍历
观察二叉树的结构,可以发现,为什么二叉树的遍历要用递归实现呢?也就是在问,为什么二叉树的遍历要用栈来实现?那是因为,二叉树的结构,只有从父节点到孩子节点的路径,却没有孩子节点到父节点的路径,一旦从父节点遍历到孩子节点,就无法返回了,那么,如果想返回,就需要一种能将从上到下的结构逆序的数据结构,那么特征就很明显了,栈就是能将数据顺序逆序过来的过程,而递归本质就是栈,因此,二叉树的遍历用递归是最容易实现的。
递归的实质,其实就是系统帮我们实现栈的结构,来保存数据。只不过,系统的栈很复杂,压栈时会保存所有状态。其实,递归时,每个节点要回溯三次的,而这些,系统栈都帮我们做好了。那么,如果我们想要实现非递归的遍历,其实就是想办法不用系统栈,自己实现栈的过程。而我们自己实现的栈,其实只需要保存有用的数据就行了。
非递归前序遍历
前序遍历,其实就是实现:中、左、右。他自己吃饱喝足了,才能轮到他孩子。每当拿到一个节点,先打印,然后将其右节点先入栈,再入栈左节点,然后再出栈,就能实现中、左、右的顺序。
//非递归前序遍历
public static void preOrderUnRecur(TreeNode head){
if(head==null){
return;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(head);
while (!stack.isEmpty()){
head=stack.pop();
System.out.println(head.value);
if(head.right!=null){
stack.push(head.right);
}
if(head.left!=null){
stack.push(head.left);
}
}
}
非递归中序遍历
中序遍历就是实现左、中、右。就是无限宠溺左孩子,左孩子再宠溺左孩子,右孩子后娘养的,排最后。
当我们拿到一个节点,不能先打印,因为要先打印他的左孩子,那就先将他压入栈中,然后来到他的左孩子,如果他的左孩子还有左孙子,那他的左孩子又不能先打印了,只能先压入栈,打印他的左孙子,依次往复。当某个节点左孩子为空,或者左孩子打印好了,就返回他自己,然后打印,最后才去他的右孩子。
//非递归中序遍历
public static void inOrderUnRecur(TreeNode head){
if(head==null){
return;
}
Stack<TreeNode> stack=new Stack<>();
while (!stack.isEmpty()||head!=null){
if(head!=null){
stack.push(head);
head=head.left;
}else {
head=stack.pop();
System.out.println(head.value);
head=head.right;
}
}
}
非递归实现后续遍历
非递归实现后续遍历最简答的方法就是依次入队列。当然,这里的话,既然是递归改非递归,底层原理还是用栈来改非递归。
先考虑这样一个问题,前面非递归前序遍历中,根节点打印后,先入栈右孩子,再入栈左孩子,就能实现中左右的顺序,那么,实现中右左呢?那不就改个顺序的事情吗?打印根节点,先入栈左孩子,再入栈右孩子,然后出栈,就是中右左。
有了中右左,那么,后序遍历是什么顺序呢?左右中对吧?发现了什么?后序遍历其实就是中左右倒过来了。倒序结构,想到了什么?没错,多一个栈就能轻松实现了。
//非递归后序遍历
public static void aftOrderUnRecur(TreeNode head){
if(head==null){
return;
}
Stack<TreeNode> s1=new Stack<>();
Stack<TreeNode> s2=new Stack<>();
s1.push(head);
while (!s1.isEmpty()){
head=s1.pop();
s2.push(head);
if(head.left!=null){
s1.push(head.left);
}
if(head.right!=null){
s1.push(head.right);
}
}
while (!s2.isEmpty()){
System.out.println(s2.pop().value);
}
}