数据结构-二叉树(先序、中序、后序遍历二叉树的非递归算法)

 

文章目录

 

思路

上一篇博文咱们讲了二叉树的递归算法,这里咱们来写一波二叉树的非递归算法java

为何说遍历二叉树能够用递归node

二叉树每一个结点都知足某个遍历次序,而后从根结点开始遍历,这个流程很是知足递归的模型,就是一个大的问题按照某种方式能够划分为许多细小的问题,而后这些细小的问题又能够用一样的方式继续划分,直到为空或者说直到有个出口算法

全部递归能够转化成非递归数组

那非递归我该如何实现呢?这就不得不从递归的有一个特色提及了,递归老是这样的形式: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 的次序,这不就是栈的数据结构吗???对了,就是栈,所以若是咱们不用递归,那咱们确定得用循环再加上栈的使用数据结构

先序&中序和后序有什么不一样ide

咱们知道先序是根左右,中序是左根右,后序是左右根,先序和中序没啥大的花样,可是后续有些不一样,为何呢?由于对于先序遍从来讲,最早记录结点的数据,而后能找到左结点就一直找左结点,找不到能够再找栈顶的右结点,并同时释放栈顶结点;若是是中序的话,先找左结点,找不到为止,就记录栈顶结点的值,释放栈顶结点,同时再去找该栈顶结点的右结点;可是对后序遍历可不一样了,由于后序是先去找左结点,一直到找不到为止,咱们再去找栈顶的右结点,可是要注意的是后序遍历在左右结点转换的时候,咱们并不知道这个根结点何时弹出栈顶,最大的不一样就在于这里。先序和中序在左结点找不到时切换右结点时候会弹出根结点,根结点这颗棋子已经没有用了,可是后序遍历可不这样,当左结点实在找不到时候,去找栈顶的右结点,此时栈顶结点还不能获得释放,由于右结点的后序遍历没有找尽每个结点,因此还不能记录该根结点的值post

后序遍历算法的思路this

若是咱们找的到左结点就一直找,找不到的话,咱们拿到栈顶结点,看栈顶的右结点是否找的到,找的到的话,那就指向右结点,而后继续按照后序遍历的模式,若是栈顶的右结点找不到,那么咱们就记录栈顶的结点,并弹出栈顶结点,而后咱们将新结点令为空,这样下次会重新栈顶结点往右边找。这样这个算法好像就完了是否是?不是,这里有个很大的漏洞!我举个例子说明:假如咱们一直找左结点找到了 a 结点,而后 a 结点的左结点为 null,a 结点的右结点是 b 结点,b 结点的左结点为 null,b 结点的右结点也为 null,当咱们找到 a 结点时候,按照上面的算法思路,a->left 为空,可是栈顶结点 a 的右结点 b 不为空,因此新结点是 b 并入栈,b 的左结点为空,而后栈顶结点 b 的右结点为空,b 又变成了 a 结点,发现了吗?这不返回的了吗?这不从 a 到 b,b 又到 a,这不没完没了了?因此算法漏洞是咱们少加了一个东西,咱们应该在当咱们右结点找不到时,须要记录栈顶的结点,而且当下一次循环访问右结点时,右结点不只不为空,并且右结点不能是上次循环中弹出的那个结点!code

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;
    }
}