思路
上一篇博文我们讲了二叉树的递归算法,这里我们来写一波二叉树的非递归算法
为什么说遍历二叉树可以用递归
二叉树每个结点都满足某个遍历次序,然后从根结点开始遍历,这个流程非常满足递归的模型,就是一个大的问题按照某种方式可以划分为许多细小的问题,然后这些细小的问题又可以用同样的方式继续划分,直到为空或者说直到有个出口
所有递归可以转化成非递归
那非递归我该如何实现呢?这就不得不从递归的有一个特点说起了,递归总是这样的形式:a<b<c<d
,a 表示第一层方法,b 表示第二层的递归,c 表示第三层的递归,d 表示第四层的递归,那么我们程序运行的效果肯定是:a 到 b 到 c 到 d,然后 d 找到出口全部执行完了,返回 c,c 又全部执行完了,再到 b,b 全部执行完了,再到 a,也就是 a→b→c→d→d→c→b→a 的次序,这不就是栈的数据结构吗???对了,就是栈,因此如果我们不用递归,那我们肯定得用循环再加上栈的使用
先序&中序和后序有什么不同
我们知道先序是根左右,中序是左根右,后序是左右根,先序和中序没啥大的花样,但是后续有些不同,为什么呢?因为对于先序遍历来讲,最先记录结点的数据,然后能找到左结点就一直找左结点,找不到可以再找栈顶的右结点,并同时释放栈顶结点;如果是中序的话,先找左结点,找不到为止,就记录栈顶结点的值,释放栈顶结点,同时再去找该栈顶结点的右结点;但是对后序遍历可不同了,因为后序是先去找左结点,一直到找不到为止,我们再去找栈顶的右结点,但是要注意的是后序遍历在左右结点转换的时候,我们并不知道这个根结点什么时候弹出栈顶,最大的不同就在于这里。先序和中序在左结点找不到时切换右结点时候会弹出根结点,根结点这颗棋子已经没有用了,但是后序遍历可不这样,当左结点实在找不到时候,去找栈顶的右结点,此时栈顶结点还不能得到释放,因为右结点的后序遍历没有找尽每一个结点,所以还不能记录该根结点的值
后序遍历算法的思路
如果我们找的到左结点就一直找,找不到的话,我们拿到栈顶结点,看栈顶的右结点是否找的到,找的到的话,那就指向右结点,然后继续按照后序遍历的模式,如果栈顶的右结点找不到,那么我们就记录栈顶的结点,并弹出栈顶结点,然后我们将新结点令为空,这样下次会从新栈顶结点往右边找。这样这个算法好像就完了是不是?不是,这里有个很大的漏洞!我举个例子说明:假如我们一直找左结点找到了 a 结点,然后 a 结点的左结点为 null,a 结点的右结点是 b 结点,b 结点的左结点为 null,b 结点的右结点也为 null,当我们找到 a 结点时候,按照上面的算法思路,a->left 为空,但是栈顶结点 a 的右结点 b 不为空,所以新结点是 b 并入栈,b 的左结点为空,然后栈顶结点 b 的右结点为空,b 又变成了 a 结点,发现了吗?这不返回的了吗?这不从 a 到 b,b 又到 a,这不没完没了了?所以算法漏洞是我们少加了一个东西,我们应该在当我们右结点找不到时,需要记录栈顶的结点,并且当下一次循环访问右结点时,右结点不仅不为空,而且右结点不能是上次循环中弹出的那个结点!
Java 实现
// 结点
class Node {
int data;
Node left = null;
Node right = null;
}
// 二叉树
public class BinaryTree {
// 根结点
private Node root;
// 输入的数组
private int[] arr_in;
// 输出的数组
private int[] arr_out;
// 记录数组下标
private static int index;
// 初始化
public BinaryTree(int[] arr) {
root = new Node();
this.arr_in = arr;
arr_out = new int[arr.length];
index = 0;
}
// 先序遍历二叉树(非递归)根→左→右
public int[] preorderTraversal(Node r) {
Stack stack = new Stack();
Node node = r;
while (!stack.empty() || node != null) {
if (node != null) {
stack.push(node);
// 根
arr_out[index++] = node.data;
// 左
node = node.left;
}
else {
Node top = stack.pop();
// 右
node = top.right;
}
}
index = 0;
return arr_out;
}
// 中序遍历二叉树(非递归)左→根→右
public int[] inorderTraversal(Node r) {
Stack stack = new Stack();
Node node = r;
while (!stack.empty() || node != null) {
if (node != null) {
stack.push(node);
// 左
node = node.left;
}
else {
Node top = stack.pop();
// 根
arr_out[index++] = top.data;
// 右
node = top.right;
}
}
index = 0;
return arr_out;
}
// 后序遍历二叉树(非递归)左→右→根
public int[] postorderTraversal(Node r) {
Stack stack = new Stack();
Node node = r;
Node top = null;
while (!stack.empty() || node != null) {
if (node != null) {
stack.push(node);
// 左
node = node.left;
}
else {
if (stack.peek().right != null && stack.peek().right != top)
// 右
node = stack.peek().right;
else {
top = stack.pop();
// 根
arr_out[index++] = top.data;
node = null;
}
}
}
index = 0;
return arr_out;
}
}