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

文章目录

思路

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

为什么说遍历二叉树可以用递归

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

所有递归可以转化成非递归

那非递归我该如何实现呢?这就不得不从递归的有一个特点说起了,递归总是这样的形式: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) {
            if (node) {
                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) {
            if (node) {
                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) {
            if (node) {
                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;
    }
}
发布了197 篇原创文章 · 获赞 62 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/abcnull/article/details/104449981
今日推荐