美文网首页数据结构和算法分析小面试程序员
二叉树的非递归前、中、后续遍历

二叉树的非递归前、中、后续遍历

作者: iszhenyu | 来源:发表于2018-05-16 11:48 被阅读49次

定义树的节点如下

public class TreeNode {
    public Integer data;
    public TreeNode leftChild;
    public TreeNode rightChild;
}

非递归前序遍历

方法一

考虑一般情况,对于给定的一个节点,可以按下面三个步骤遍历:

1、持续遍历左子节点,直到左子节点为空。
2、弹出栈顶元素,访问它的右子节点。
3、继续第一步,直到栈空。

public List<TreeNode> preOrder(TreeNode root) {
    List<TreeNode> result = new ArrayList<>();
    if (root == null) {
        return result;
    }

    TreeNode cur = root;
    Deque<TreeNode> stack = new LinkedList<>();
    while (cur != null || !stack.isEmpty()) {
        // 持续遍历左子树,直到左子树为空
        while (cur != null) {
            result.add(cur);
            stack.push(cur);
            cur = cur.leftChild;
        }
        if (!stack.isEmpty()) {
            cur = stack.pop();
            cur = cur.rightChild;
        }
    }

    return result;
}
方法二

根据栈的弹出顺序来遍历,考虑下面三个步骤

1、利用给定的节点初始化栈,保证栈不为空。
2、访问栈顶元素,然后将其右子节点、左子节点分别入栈。
3、重复步骤2,直到栈为空。

public List<TreeNode> preOrder(TreeNode root) {
    List<TreeNode> result = new ArrayList<>();
    if (root == null) {
        return result;
    }

    TreeNode cur = root;
    Deque<TreeNode> stack = new LinkedList<>();
    // 首先跟节点入栈,保证栈不为空
    stack.push(cur);
    while (!stack.isEmpty()) {
        // 访问栈顶元素
        cur = stack.pop();
        result.add(cur);
        // 右子节点、左子节点分别入栈
        if (cur.rightChild != null) {
            stack.push(cur.rightChild);
        }
        if (cur.leftChild != null) {
            stack.push(cur.leftChild);
        }
    }

    return result;
}

非递归中序遍历

这里采用的方法与前序遍历中介绍的第一种方法类似:

1、持续遍历左子节点,只入栈不访问,直到左子节点为空。
2、弹出栈顶元素,这个时候访问该节点,访问它的右子节点。
3、继续第一步,直到栈空。

public List<TreeNode> inOrder(TreeNode root) {
    List<TreeNode> result = new ArrayList<>();
    if (root == null) {
        return result;
    }

    TreeNode cur = root;
    Deque<TreeNode> stack = new ArrayDeque<>();
    while (cur != null || !stack.isEmpty()) {
        // 只入栈,不访问
        while (cur != null) {
            stack.push(cur);
            cur = cur.leftChild;
        }
        // 出栈的时候访问
        if (!stack.isEmpty()) {
            cur = stack.pop();
            result.add(cur);
            cur = cur.rightChild;
        }
    }

    return result;
}

非递归后序遍历

非递归遍历被称为三种遍历中最难的一个,这里,我们分别用三种方法来实现

方法一

后序遍历可以看做是从右到左的先序遍历的逆过程,所以可以利用辅助栈,按照从右至左的先序遍历,遍历的结果存到辅助栈里,然后将辅助栈的元素依次出栈。

public List<TreeNode> postOrder(TreeNode root) {
    List<TreeNode> result = new ArrayList<>();
    if (root == null) {
        return result;
    }

    TreeNode cur = root;
    // 辅助栈
    Deque<TreeNode> assistStack = new ArrayDeque<>();
    // 用于保存从右到左先序遍历节点的栈
    Deque<TreeNode> stack = new ArrayDeque<>();
    stack.push(cur);

    while (!stack.isEmpty()) {
        cur = stack.pop();
        assistStack.push(cur);
        if (cur.leftChild != null) {
            stack.push(cur.leftChild);
        }
        if (cur.rightChild != null) {
            stack.push(cur.rightChild);
        }
    }
    // 将辅助栈元素依次出栈
    while (!assistStack.isEmpty()) {
        cur = assistStack.pop();
        result.add(cur);
    }
    return result;
}
方法二

对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点。

此时该结点出现在栈顶,但是此时不能将其出栈并访问,因为其右孩子还未被访问。所以,接下来按照相同的规则对其右子树进行相同的处理。当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。

在这个过程中,每个结点都两次出现在栈顶,只有在第二次出现在栈顶时,才能访问它。

public List<TreeNode> postOrder3() {
    List<TreeNode> result = new ArrayList<>();
    if (root == null) {
        return result;
    }
    // 用来保存当前节点是第几次访问
    Map<TreeNode, Integer> visitedNodes = new HashMap<>();
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode cur = root;
    while (cur != null || !stack.isEmpty()) {
        while (cur != null) {
            visitedNodes.put(cur, 1);
            stack.push(cur);
            cur = cur.leftChild;
        }

        if (!stack.isEmpty()) {
            cur = stack.pop();
            int visitCount = visitedNodes.getOrDefault(cur, 0);
            // 第二次出现在栈顶才访问
            if (visitCount < 2) {
                visitedNodes.put(cur, visitCount + 1);
                stack.push(cur);
                cur = cur.rightChild;
            } else {
                result.add(cur);
                cur = null;
            }
        }
    }
    return result;
}
方法三

根据后续遍历的访问情况,我们可以总结出一般规律,对于任一结点P,有且只有两种情况下才可以对其访问:

1、如果P不存在左孩子和右孩子,则可以直接访问它;

2、P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。

若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。

public List<TreeNode> postOrder4() {
    List<TreeNode> result = new ArrayList<>();
    if (root == null) {
        return result;
    }

    TreeNode cur = root;
    // 保存上一个访问的节点,
    // 用来判断上一个访问的节点是不是当前节点的左子节点或右子节点
    TreeNode pre = null;
    Deque<TreeNode> stack = new ArrayDeque<>();
    stack.push(cur);
    while (!stack.isEmpty()) {
        cur = stack.peek();
        // 1)叶子节点直接访问
        // 2)如果当前节点要被加到result中,则一定满足:
        //     1、左子节点刚刚被添加(没有右子节点的情况)
        //     2、右子节点刚刚被添加(有或没有左子节点)
        if ((cur.leftChild == null && cur.rightChild == null) ||
            (pre != null && (pre == cur.leftChild || pre == cur.rightChild))) {
            result.add(cur);
            stack.pop();
            pre = cur;
        } else {
            if (cur.rightChild != null) {
                stack.push(cur.rightChild);
            }
            if (cur.leftChild != null) {
                stack.push(cur.leftChild);
            }
        }
    }
    return result;
}

完!

相关文章

  • 二叉树遍历

    请递归,非递归方式分别前序遍历,中序遍历,后续遍历二叉树

  • 二叉树遍历java,非递归、层次。

    /** * 前序遍历 * 递归 */ /*** 前序遍历* 非递归*/ 后续遍历非递归 二叉树层次遍历基于java...

  • 二叉树的相关操作(一)

    上次写了二叉树遍历,其中在非递归的遍历中,只写了前序遍历,没有写非递归中序遍历和后续遍历。非递归要用到栈,只要根据...

  • 二叉树的三种遍历(前序/中序/后序)

    参考博客 二叉树的非递归后续遍历

  • 左神算法笔记——Morris遍历

    基本问题——实现二叉树的前序、中序、后序遍历 (递归、非递归,mirros方法) 递归 递归方式下的前中后遍历 非...

  • 二叉树递归非递归遍历算法整理

    一、二叉树前序遍历 1 前序递归遍历 2.前序非递归遍历 一、二叉树中序遍历 2.中序递归遍历 1.中序非递归遍历...

  • 大数据面试题目

    一、数据结构与算法 1.二叉树前序、中序、后续遍历方式(递归以及非递归) 2.二叉树的深度以及广度遍历方式 ...

  • 二叉树的遍历(递归和非递归)

    1. 二叉树的遍历 前序遍历:根、左、右中序遍历:左、根、右后续遍历:左、右、根 2.递归以及非递归的实现 因为二...

  • 总结

    1、二叉树广度遍历(非递归) 广度遍历非递归实现需要依靠一个队列。 2、二叉树深度遍历(递归与非递归,前序,中序和...

  • 二叉树遍历

    二叉树的遍历 1. 前序遍历 1.1 递归前序遍历 1.2 非递归前序遍历 2 中序遍历 2.1递归遍历 2.2非...

网友评论

    本文标题:二叉树的非递归前、中、后续遍历

    本文链接:https://www.haomeiwen.com/subject/safpdftx.html