二叉树前序、中序、后序、层序遍历(递归/非递归)

二叉树的前序、中序、后序、层序、之字形遍历等是数据结构和算法中很基础和重要的一部分。使用递归方法我们可以很直观和方便地写出二叉树的各种遍历,但是递归会咋成一些多余的内存空间的浪费(每次调用函数都会在栈中创建栈帧,保存参数)。使用非递归的方法进行二叉树的遍历稍微有些麻烦,但是相对地节约了内存空间,也是笔试/面试中经常考的,下面我们来看一下各种遍历方法。

二叉树的前序、中序、后序指的是根节点的顺序。前序遍历是先输出根结点,然后依次访问左子树和右子树(每个子树都是这样的顺序)。中序遍历是先访问左子树,输出根节点,然后访问右子树。后序遍历是先访问左子树,再访问右子树,最后输出根节点。
层序遍历中,对二叉树逐层输出。

二叉树的结点

我们定义二叉树的结点如下,每个结点包含一个int型值,左子结点和右子结点的引用。

public class TreeNode {
    int value=0;
    TreeNode left;
    TreeNode right;
}

二叉树的前序遍历

在二叉树的前序遍历中,先输出根节点的值,然后依次访问左子树和右子树,每一个子树都是这样的顺序。

递归

根据这个思路,我们很容易地写出了前序遍历的递归方法

    public List<Integer> PreOrderSearch(TreeNode head){
        List<Integer> res= new ArrayList<>();
        loop(res,head);
        return res;
    }
    
    private void loop(List<Integer> res,TreeNode node) {
        if(node == null)
            return ;
        res.add(node.value);    //输出根节点
        loop(res,node.left);      //访问左子树
        loop(res,node.right);    //访问右子树
    }
非递归

使用非递归方法来前序遍历二叉树,我们可以使用栈。对于每一个子树,我们都先输出其根节点,然后依次将右子结点和左子结点压缩栈中,不断地弹出结点,输出结点值,然后将右子结点和左子结点压栈,最后完成二叉树的前序遍历。

public List<Integer> PreOrderSearch(TreeNode head) {
        List<Integer> res= new ArrayList<>();
        if(head == null)
            return res;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode cur = head;
        stack.push(cur);
        while(!stack.isEmpty()) {
            cur = stack.pop();
            res.add(cur.value);     //输出结点值
            if(cur.right != null)      //将右子结点压栈
                stack.push(cur.right);
            if(cur.left != null)       //将左子结点压栈
                stack.push(cur.left);
        }
        return res;
    }

中序遍历

在二叉树的中序遍历中,先访问左子树,再输出节点的值,然后访问右子树,每一个子树都是这样的顺序。

递归
    public List<Integer> InOrderSearch(TreeNode head){
        List<Integer> res= new ArrayList<>();
        loop(res,head);
        return res;
    }
    
    private void loop(List<Integer> res,TreeNode node) {
        if(node == null)
            return ;
        loop(res,node.left);       //先访问左子树
        res.add(node.value);    //输出结点值
        loop(res,node.right);    //访问右子树
    }
非递归

我们同样可以用栈来实现非递归中序遍历。优点类似深度优先搜索和回溯。原理用语言不容易说清楚,大家仔细看以下代码。

    public List<Integer> InOrderSearch(TreeNode head){
        List<Integer> res = new ArrayList<Integer>();
        if(head == null)
            return res;
        TreeNode cur = head;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        while(cur != null || !stack.isEmpty()){
            while(cur != null) {                   //将结点及其左子结点压栈
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();               //从栈中弹出一个结点,输出结点值,如果该结点有右子结点,移动指针cur到右子结点,重新开始压栈操作。
            res.add(cur.value);
            cur = cur.right;
        }
        return res;
    }

后序遍历

递归
    public List<Integer> PostOrderSearch(TreeNode head){
        List<Integer> res= new ArrayList<>();
        loop(res,head);
        return res;
    }
    
    private void loop(List<Integer> res,TreeNode node) {
        if(node == null)
            return ;
        loop(res,node.left);                    //先访问左子树
        loop(res,node.right);                //然后访问右子树
        res.add(node.value);            //最后输出根结点
    }
非递归

我们依然使用栈来实现,我们还需要维护一个指针pre,指向前一个访问的结点。具体看代码。

  public List<Integer> PostOrderSearch(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;
        Stack<TreeNode> stack = new Stack();
        TreeNode cur = root;           //指针,指向当前结点
        TreeNode pre = null;           //指针,指向前一个访问的结点
        while(!stack.isEmpty()||cur != null){
            while(cur != null){                //首先,将每一个结点和其左子结点压栈
                stack.push(cur);
                cur = cur.left;
            }
            pre = null;
            while(!stack.isEmpty()){
                cur = stack.peek();          
                if(cur.right == pre || cur.right == null){    //如果当前栈顶元素的右子结点已经访问过或者右子结点为null,输出本结点;否则将当前指针指向右子结点,重新压栈。
                    cur = stack.pop();
                    res.add(cur.value);
                    pre = cur;
                    cur = null;
                }else{
                    cur = cur.right;
                    break;
                }
            }
        }
        return res;
    }

层序遍历

递归

递归实现是对二叉树进行深度优先搜索,然后使用一个int值来保存当前的层数。

    public List<List<Integer>> levelOrderSearch(TreeNode root) {
        List<List<Integer>>list= new ArrayList<List<Integer>>();
        addlevel(list,0,root);
        return list;
    }
    public void addlevel(List<List<Integer>>list,int level,TreeNode node){
        if(node==null) return;
        if(list.size()-1<level) list.add(new ArrayList<Integer>());
        list.get(level).add(node.value);
        addlevel(list,level+1,node.left);
        addlevel(list,level+1,node.right);
    }
非递归

非递归实现中使用队列来对二叉树进行广度优先搜索,并且维护两个int值来保存当前层中结点数和下一层中节点数。

public List<List<Integer>> LevelOrderSearch(TreeNode node){
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if(node == null)
            return res;
        TreeNode cur = null;
        int curLevelCount = 1;
        int nextLevelCount = 0;
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(node);
        res.add(new ArrayList<Integer>());
        while(!queue.isEmpty()) {
            cur = queue.poll();
            res.get(res.size()-1).add(cur.value);
            curLevelCount--;
            if(cur.right != null) {
                queue.offer(cur.right);
                nextLevelCount++;
            }
            if(cur.left != null) {
                queue.offer(cur.left);
                nextLevelCount++;
            }
            if(curLevelCount <= 0) {
                curLevelCount = nextLevelCount;
                nextLevelCount = 0;
                if(curLevelCount>0)
                    res.add(new ArrayList<Integer>());
            }
        }
        return res;
    }
    原文作者:小怪兽LY
    原文地址: https://www.jianshu.com/p/f0216731c52e
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞