前言
在之前的文章里介绍过如何使用递归的方式来实现二叉树的先序、中序、后序遍历,这篇文章来介绍下如何采用非递归的方式实现二叉树的先序、中序、后序遍历。
正文
其实任何递归函数都可以用非递归的方式来实现,递归函数在系统上实现时涉及到函数的入栈和出栈,既然要用非递归的方式来实现,这就需要我们自己来实现入栈和出栈。
如图所示,图中二叉树:
- 先序遍历为:
1->2->4->5->3->6->7 - 中序遍历为:
4->2->5->1->6->3->7 - 后序遍历为:
4->5->2->6->7->3->1
下面来一起分析下使用非递归的二叉树先序、中序、后序遍历。
先序遍历
先序遍历的顺序是:访问根节点(头)->遍历左子树(左)->遍历右子树(右),简称:头->左->右。
使用非递归时,先准备一个栈:
- 先把头结点压栈,毫无道理可言,
节点1入栈。 - 将栈顶元素
节点1弹出,记为current,节点1出栈。 - 弹出的节点如果有右节点则将右节点入栈,如果有左节点后将左节点入栈,先右后左,
节点3先入栈,节点2后入栈。 - 将
节点2弹出,将节点5入栈,将节点4入栈。 - 将
节点4弹出,节点4没有子节点,直接弹出,不入栈。 - 将
节点5弹出,节点5没有子节点,直接弹出,不入栈。 - 将
节点3弹出,将节点7入栈,将节点6入栈。 - 将
节点6弹出,节点6没有子节点,直接弹出,不入栈。 - 将
节点7弹出,节点7没有子节点,直接弹出,不入栈。 - 栈中没有节点了,结束。
从上面节点弹出的顺序即为二叉树的先序遍历,可以将过程简化如下:
- 先将根节点入栈
- 弹出栈顶元素,并用一个变量记录。
- 先将弹出元素的右节点入栈,后将左节点入栈。
循环步骤2、3,直到栈中没有元素。
代码实现
public static void pre(Node head) {
if (head != null) {
Stack<Node> stack = new Stack<>();
stack.push(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();
}
中序遍历
中序遍历的顺序是:遍历左子树(左)->访问根节点(头)->遍历右子树(右),简称:左->头->右。
用栈来实现中序遍历有点麻烦,流程如下:
- 先把一个数的所有左边界从上到下进行压栈,
1->2->4。 - 弹出
节点4并打印节点4的值,判断节点4的右节点是否为空,为空的话,继续弹出。 - 弹出
节点2并打印节点2的值,将节点2的右节点——节点5以及其所在子树的所有左边界入栈。 - 弹出
节点5并打印节点5的值,节点5无右节点,继续弹出。 - 弹出
节点1并打印节点1的值,并将节点3、节点6入栈。 - 弹出
节点6并打印节点6的值,节点6无右节点,继续弹出。 - 弹出
节点3并打印节点3的值,并将节点7入栈。 - 弹出
节点7并打印节点7的值,节点7无右节点,继续弹出。 - 栈中所有几点全部弹出,此时中序遍历完成。
从上面节点弹出的顺序即为二叉树的中序遍历,可以将过程简化如下:
- 先将根节点记为
current,current节点的所在左边界节点入栈。 - 弹出栈顶元素,并打印节点值。
- 将弹出节点的右节点作为
current,重复步骤1~3。
代码实现
public static void in(Node current) {
if (current != null) {
Stack<Node> stack = new Stack<>();
while (!stack.isEmpty() || current != null) {
if (current != null) {
stack.push(current);
current = current.left;
} else {
current = stack.pop();
System.out.print(current.value + " ");
current = current.right;
}
}
}
System.out.println();
}
后序遍历
后序遍历的顺序是:遍历左子树(左)->遍历右树(右)->访问根节点(头),简称:左->右->头。
后序遍历和先序遍历有点相似,先序遍历的顺序为“头->左->右”,更改下节点放入顺序就是"头->右->左",再把"头->右->左"逆序就得到了“左->右->头”。
来看下具体过程,根据先序遍历的步骤,入栈时先将右节点入栈后将左节点入栈,我们稍微改造下,先将左节点入栈,后将右节点入栈。
在弹出节点时,先不将节点的值打印出来,而是准备一个新栈,将弹出的节点放入新栈中,等旧栈所有元素都弹出完,再将新栈的元素弹出并打印,这时打印的顺序即为后序遍历。
代码实现
public static void pos(Node head) {
if (head != null) {
Stack<Node> stack1 = new Stack<>();
Stack<Node> stack2 = new Stack<>();
stack1.push(head);
while (!stack1.isEmpty()) {
head = stack1.pop();
stack2.push(head);
if (head.left != null) {
stack1.push(head.left);
}
if (head.right != null) {
stack1.push(head.right);
}
}
while (!stack2.isEmpty()) {
System.out.print(stack2.pop().value + " ");
}
}
System.out.println();
}
总结
本篇文章主要介绍如何采用非递归的方式实现二叉树的先序、中序、后序遍历,其中在实现后序遍历的时候,采用了两个栈,其实也可以用一个栈实现的,不过相对复杂点,这里就先不介绍了~