二叉树的几种遍历方法(前序、中序、后序)

二叉树遍历详解
本文详细介绍了二叉树的前序、中序、后序遍历方法,包括递归和非递归实现,并深入探讨了Morris遍历算法及其在不同遍历中的应用。

二叉树的遍历一般有前序、中序、后序。

遍历二叉树几种常用的方法:

  1. 利于栈(stack)
  2. 利于递归
  3. 在保证空间利用率为O(1)时,使用Minors方法、

  1. 递归方法
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        //保存结果数组
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    }

    public void inorder(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        inorder(root.left, res);
        res.add(root.val);
        inorder(root.right, res);
    }
}

递归可以说是二叉树相对来说最简单的掌握方式,前序、中序、后序遍历主要体现在以下代码上,如果  res.add(root.val);在中间即中序遍历,在首位即前序遍历、在末尾即后序遍历。

        inorder(root.left, res);
        res.add(root.val);
        inorder(root.right, res);

        2.利用栈

利用栈的先进后出的特性,首先让二叉树遍历到最左边的位置,然后再一个节点一个节点弹出。

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        //初始化栈
        Deque<TreeNode> stk = new LinkedList<TreeNode>();
        while (root != null || !stk.isEmpty()) {
            while (root != null) {
                //将节点压入栈中
                stk.push(root);
                //一直向左走
                root = root.left;
            }
            //走到了最左边之后,将最后一个进栈的元素弹出
            root = stk.pop();
            res.add(root.val);
            //接着向右走
            root = root.right;
        }
        return res;
    }
}

利用栈遍历二叉树时间复杂度和空间复杂度一般都为O(n);且前序、中序、后续遍历的代码类似,区别主要体现在一下代码段上。

            root = stk.pop();
            res.add(root.val);
            root = root.right;

即如果 res.add(root.val);在中间就是中序遍历;前序遍历为以下代码

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        //初始化栈
        Deque<TreeNode> stk = new LinkedList<TreeNode>();
        while (root != null || !stk.isEmpty()) {
            while (root != null) {
                 res.add(root.val);
                //将节点压入栈中
                stk.push(root);
                //一直向左走
                root = root.left;
            }
            //走到了最左边之后,将最后一个进栈的元素弹出
            root = stk.pop();
            //接着向右走
            root = root.right;
        }
        return res;
    }
}

 后续遍历为一下代码

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if (root == null) {
            return res;
        }

        Deque<TreeNode> stack = new LinkedList<TreeNode>();
        TreeNode prev = null;
        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            if (root.right == null || root.right == prev) {
                res.add(root.val);
                prev = root;
                root = null;
            } else {
                stack.push(root);
                root = root.right;
            }
        }
        return res;
    }
}

4.Morris 中序遍历

主要详细介绍下中序遍历的过程

  • 首先第一步会有一个空节点predecessor该节点的主要作用就是将二叉树的叶子节点与当前遍历的root节点建立联系
  • 在第一步时,predecessor节点先向左走一步,保证二叉树左子数先于右子数遍历,如果此时root的左孩子为空,则将当前root节点加入到结果集合中去,root节点向右走一步。
  • 接着predecessor一直往右走,直到走到当前root的左子树最右的叶子节点,这一步要保证predecessor不为空,且它右孩子不指向当前的root
  • 接着进行判断,由于predecessor位于当前root的左孩子的最右边的叶子节点,如果predecessor为空则表示为第一次访问该节点,将该叶子节点与root建立联系,即将该叶子节点的右孩子指向root,完成之后,将root节点向左走一位,接着从第一步开始,在当前root节点下与叶子节点建立联系。
    • 如果此时predecessor不为空,则表明是第二次访问该节点,此时需要将他与当前root节点建立的联系断开,将他加入到遍历集合中去,接着root要向右走一步,方位他的右子树。

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        TreeNode predecessor = null;

        while (root != null) {
            if (root.left != null) {
                // predecessor 节点就是当前 root 节点向左走一步
                predecessor = root.left;
                //接着一直向右走
                while (predecessor.right != null && predecessor.right != root) {
                    predecessor = predecessor.right;
                }
                
                // 让 predecessor 的右指针指向 root,继续遍历左子树
                if (predecessor.right == null) {
                    //最后一个节点的right指向当前子树的root
                    predecessor.right = root;
                    //接着root向左走一步
                    root = root.left;
                }
                // 说明左子树已经访问完了,我们需要断开链接
                else {
                    //此时将root节点加入到集合中去
                    res.add(root.val);
                    //断开节点的链接
                    predecessor.right = null;
                    //开始访问当前root的right子数
                    root = root.right;
                }
            }
            // 如果没有左孩子,则直接访问右孩子
            else {
                //走到了当前二叉树root的左节点
                res.add(root.val);
                root = root.right;
            }
        }
        return res;
    }
}

5. Morris 前序遍历

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if (root == null) {
            return res;
        }

        TreeNode p1 = root, p2 = null;

        while (p1 != null) {
            p2 = p1.left;
            if (p2 != null) {
                while (p2.right != null && p2.right != p1) {
                    p2 = p2.right;
                }
                if (p2.right == null) {
                    res.add(p1.val);
                    p2.right = p1;
                    p1 = p1.left;
                    continue;
                } else {
                    p2.right = null;
                }
            } else {
                res.add(p1.val);
            }
            p1 = p1.right;
        }
        return res;
    }
}

6.Morris 后序遍历 

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if (root == null) {
            return res;
        }

        TreeNode p1 = root, p2 = null;

        while (p1 != null) {
            p2 = p1.left;
            if (p2 != null) {
                while (p2.right != null && p2.right != p1) {
                    p2 = p2.right;
                }
                if (p2.right == null) {
                    p2.right = p1;
                    p1 = p1.left;
                    continue;
                } else {
                    p2.right = null;
                    addPath(res, p1.left);
                }
            }
            p1 = p1.right;
        }
        addPath(res, root);
        return res;
    }

    public void addPath(List<Integer> res, TreeNode node) {
        int count = 0;
        while (node != null) {
            ++count;
            res.add(node.val);
            node = node.right;
        }
        int left = res.size() - count, right = res.size() - 1;
        while (left < right) {
            int temp = res.get(left);
            res.set(left, res.get(right));
            res.set(right, temp);
            left++;
            right--;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值