遍历二叉树

743 阅读1分钟

1、三种顺序遍历

        二叉树进行遍历时,会将节点变量都会保存在栈帧中,直到整个函数执行完毕时,才会将节点从栈帧中弹出。也可以用pre保存先前结点的值。

private List<Integer> results = new ArrayList<>();

**先序遍历:**根节点,左子树,右子树。

public void travesal(TreeNode root) {
    if (root == null) return;
    results.add(root.val);
    travesal(root.left);
    travesal(root.right);
}

**中序遍历:**左子树,根节点,右子树。

public void travesal(TreeNode root) {
    if (root == null) return;
    travesal(root.left);
    results.add(root.val);
    travesal(root.right);
}

****后序遍历:****左子树,右子树,根节点。

public void travesal(TreeNode root) {
    if (root == null) return;
    travesal(root.left);
    travesal(root.right);
    results.add(root.val);

2、四种中序遍历方法

Morris中序遍历🐂🐂🐂

        空间复杂度为常数,执行步骤多。

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    //首先把根节点赋值给cur
    TreeNode cur = root;
    //如果cur不为空就继续遍历
    while (cur != null) {
        if (cur.left == null) {
            //如果当前节点cur的左子节点为空,就访问当前节点cur,
            //接着让当前节点cur指向他的右子节点
            res.add(cur.val);
            cur = cur.right;
        } else {
            TreeNode pre = cur.left;
            //查找pre节点,注意这里有个判断就是pre的右子节点不能等于cur
            while (pre.right != null && pre.right != cur)
                pre = pre.right;
            //如果pre节点的右指针指向空,我们就让他指向当前节点cur,
            //然后当前节点cur指向他的左子节点
            if (pre.right == null) {
                pre.right = cur;
                cur = cur.left;
            } else {
                //如果pre节点的右指针不为空,那么他肯定是指向cur的,
                //表示cur的左子节点都遍历完了,我们需要让pre的右
                //指针指向null,目的是把树给还原,然后再访问当前节点
                //cur,最后再让当前节点cur指向他的右子节点。
                pre.right = null;
                res.add(cur.val);
                cur = cur.right;
            }
        }

递归方法

        递归方法的核心就是栈,每个函数都有自己栈帧,即使函数自身调用自身也会生成一个栈帧。每个栈帧都会存储变量和返回地址。当二叉树进行遍历时,会将节点变量都会保存在栈帧中,直到整个函数执行完毕时,才会将节点从栈帧中弹出。

public void inOrderTraversal(TreeNode node) {
    if (node == null)
        return;
    inOrderTraversal(node.left);
    System.out.println(node.val);
    inOrderTraversal(node.right);
}

递归方法栈帧如下所示。

非递归方法

        非递归调用核心还是利用栈,就是模拟递归栈帧。被调用函数栈帧保存了上个调用函数的返回地址,可以知道每个函数执行状态,是执行到左节点被压入、右节点被压入、还是弹出栈。

public List<Integer> postorderTraversal(TreeNode root) {
    if (root == null) return  Collections.emptyList();

    Stack<TreeNode> stack = new Stack<TreeNode>();
    List<Integer> results = new ArrayList<>();
    stack.push(root);

    while(!stack.isEmpty()) {
        TreeNode top = stack.pop();
        if (top != null) {
            if (top.right != null) stack.push(top.right);
            stack.push(top);
            stack.push(null);
            if (top.left != null) stack.push(top.left);
        } else {
            results.add(stack.pop().val);
        }
    }
    return results;
}