224、使用递归和非递归实现二叉树的前、中、后序遍历
使用递归来实现二叉树的前、中、后序遍历比较简单,直接给出代码,我们重点讨论非递归的实现。
class Node {
public int value;
public Node left;
public Node right;
public Node() {
}
public Node(int value) {
this.value = value;
}
}
public class Solution {
//递归实现前序遍历
public void preOrderRecur(Node head){
if (head == null)
return;
System.out.println(head.value + " ");
preOrderRecur(head.left);
preOrderRecur(head.right);
}
//递归实现中序遍历
public void inOrderRecur(Node head){
if (head == null)
return;
inOrderRecur(head.left);
System.out.println(head.value + " ");
inOrderRecur(head.right);
}
//递归实现后续遍历
public void posOrderRecur(Node head){
if (head == null)
return;
posOrderRecur(head.left);
posOrderRecur(head.right);
System.out.println(head.value + " ");
}
}
凡是可以使用递归可以解决的问题都可以使用非递归的方法来实现。这是因为递归方法无非就是利用函数栈来保存信息,如果用自己申请的数据结构来代替函数栈,也可以实现相同的功能。
用非递归的方法实现二叉树的先序遍历,具体过程如下:
1、申请一个新的栈,记为stack。然后将头结点head压入stack中;
2、从stack中弹出栈顶节点,记为cur,然后打印cur节点的值,再将节点cur的右孩子节点(如果不为空)先压入到stack中,最后将cur的左孩子节点(如果不为空)压入stack中。
3、不断重复步骤2,直到stack为空,过程全部结束。
下面举例说明整个过程,假如存在一颗二叉树如图所示:
- 节点1先入栈,然后弹出并打印。接下来把节点3压入栈,再把节点2压入栈,此时stack从顶到底依次为2、3;
- 节点2弹出并打印,把节点5压入栈,再把节点4压入栈,此时栈中从顶到底元素依次为4、5、3;
- 节点4弹出并打印,由于节点4没有孩子节点可以压入栈,所以此时栈中元素从顶到底依次为5、3;
- 节点5弹出并打印,由于节点5没有孩子节点可以压入栈,所以此时栈中元素仅有3;
- 节点3弹出并打印,先把节点7压入栈,再把节点6压入栈,此时栈中元素从顶到底依次为6、7;
- 节点6弹出并打印,由于节点6没有孩子节点可以压入栈,所以此时栈中元素仅有7;
- 节点7弹出并打印,由于节点7没有孩子节点可以压入栈,此时stack中为空,过程停止。
整个过程请参考如下代码:
//不用递归实现前序遍历
public void preOrderUnRecur(Node head){
System.out.print("前序遍历:");
if (head != null){
Stack<Node> stack = new Stack<>();
stack.add(head);
while (!stack.isEmpty()){
head = stack.pop();
System.out.print(head.value + " ");
if (head.right != null){
stack.push(head.right);
}
if (head.left != null){
stack.push(head.left);
}
}
}
System.out.println();
}
用非递归的方式来实现二叉树的中序遍历,具体过程如下:
- 申请一个新的栈,记为stack。初始时,令变量cur = head;
- 先把cur节点压入栈中,对以cur节点为头结点的整颗子树来说,依次把左边界压入栈中,即不停的令cur= cur.left;然后重复此步骤;
- 不断重复步骤2,直到发现cur为空,此时从stack中弹出一个节点,记为node。打印node的值,并且让cur = node.right,然后继续重复步骤2;
- 当stack为空且cur为空时,整个过程停止。
还是以上图的二叉树为例子来说明整个过程:
扫描二维码关注公众号,回复:
6094036 查看本文章
- 初始时cur为节点1,将节点1压入栈,令cur= cur.left,此时cur变为节点2;
- cur为节点2,将节点2压入栈中,令cur = cur.left,即此时cur变为节点4;
- cur为节点4,将节点4压入栈中,令cur = cur.left,此时cur变为了null,此时栈中元素从顶到底依次为4、2、1;
- cur为null,从栈中弹出节点4(node)并打印,并令cur = node.right,cur仍然为null,此时stack中从顶到底依次为2、1;
- cur为null,从栈中弹出节点2(node)并打印,并令cur = node.right,此时cur变为节点5,此时栈中元素只有1;
- cur为节点5,将节点5压入stack中,并令cur = cur.left,此时cur变为null,栈中元素有5、1;
- cur为null,从栈中弹出节点5(node)并打印,并令cur = node.right,即cur仍为null,此时栈中元素为1;
- cur为null,从栈中弹出节点1(node)并打印,并令cur = node.right,即cur变为节点3,此时栈中为空;
- cur为节点3,将节点3压入栈中,令cur = cur.left,此时cur变为节点6,此时栈中仅有元素3;
- cur为节点6,将节点6压入栈中,令cur = cur.left,此时cur变为null,此时栈中有元素6、3;
- cur为null,弹出节点6(node)并打印,令cur = node.right,cur仍为null,此时栈中元素仅有3;
- cur为null,弹出节点3(node)并打印,令cur = node.right,cur变为节点7,此时栈中为空;
- cur为节点7,将节点7压入栈,令cur = cur.left,此时cur变为null,此时栈中仅有元素7;
- cur为null,弹出节点7(node)并打印,令cur = node.right,cur仍然为null,此时栈stack也为null。
- cur和stack同时为null,整个过程终止。
代码如下:
//不用递归实现中序遍历
public void inOrderUnrecur(Node head){
System.out.println("in-Order:");
if (head != null){
Stack<Node> stack = new Stack<>();
while (!stack.isEmpty() || head != null){
if (head != null){
stack.push(head);
head = head.left;
}else {
head = stack.pop();
System.out.print(head.value + " ");
head = head.right;
}
}
}
System.out.println();
}
用非递归方式实现二叉树后序遍历有点麻烦。下面介绍使用两个栈来实现后序遍历的过程,具体步骤如下:
- 申请一个栈记为s1,然后将头结点压入s1中;
- 从s1中弹出的节点记为cur,然后依次将cur的左孩子和右孩子压入s1中;
- 在整个过程中,每一个从s1中弹出的节点都放进s2中;
- 不断重复步骤2和步骤3,直到s1为空,过程停止;
- 然后从s2中依次弹出节点并打印,打印的顺序就是后序遍历的顺序。
下面还是使用上面的图示二叉树来演示整个过程:
- 申请两个栈s1和s2,将头结点1压入s1中,然后再从s1中弹出节点1,再把节点1压入s2中,再把节点2和节点3一次压入s1,此时s1从栈顶到栈底元素为3、2;s2从栈顶到栈底只有1;
- 从s1中弹出节点3,节点3放入s2中,然后将节点3的两个孩子节点6和节点7一次压入s1,此时s1从栈顶到栈底元素依次为7、6、2;s2中为3、1;
- 从s1中弹出节点7,节点7放入s2中,由于节点7没有孩子,不需要往s1中添加任何元素,此时s1中保存6、2;s2中保存7、3、1;
- 从s1中弹出节点6,节点6放入s2中,由于节点6没有孩子,不需要往s1中添加任何元素,此时s1中保存2,s2中保存6、7、3、1;
- 从s1中弹出节点2,节点2放入s2中,然后将节点2的两个孩子节点4和节点5一次压入s1中,此时s1从栈顶到栈底元素为5、4;s2中存放的元素为2、6、7、3、1;
- 从s1中弹出节点5,节点5放入s2中,节点5无孩子节点,不需要往s1中添加任何元素,此时s1中保存4;s2中保存5、2、6、7、3、1;
- 从s1中弹出节点4,节点4放入s2中,节点4无孩子节点,不需要往s1中添加任何节点,此时s1中为空,s2中保存的元素从栈顶到栈底依次是4、5、2、6、7、3、1;
- 过程结束,依次弹出并打印s2中的节点即可。
通过如上过程我们知道,每棵子树的头结点都是先从s1中弹出,然后再把该节点的两个孩子节点按照先左再右的顺序依次压入s1,那么从s1弹出的顺序就是先右后左,所以从s1中弹出的顺序就是中、右、左。然后,s2重新收集的过程就是把s1的弹出顺序逆序,所以s2从栈顶到栈底的顺序就变成了左、右、中。
代码如下:
//不用递归实现后续遍历
public void posOrderUnRecur(Node head){
System.out.println("pos-order: ");
if (head != null){
Stack<Node> s1 = new Stack<>();
Stack<Node> 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.print(s2.pop().value + " ");
}
}
System.out.println();
}