【数据结构】二叉树(二)遍历

上篇已经了解对二叉树有了大概了解,本篇学习二叉树的前序、中序、后序及层序遍历的递归与非递归共7种遍历方法,快收藏吧~

目录

1、前序遍历

递归方式:

迭代方式: 

2、中序遍历

递归方式:

 迭代方式:

3、后序遍历:

递归方式

迭代方式:

4、层序遍历

递归方式

迭代方式: 


二叉树的前序、中序、后序及层序遍历的递归与非递归遍历方法

遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结 点均做一次且仅做一次访问访问结点所做的操作依赖于具体的应用问题(比如:打印节点内容、节点内容加 1)。 遍历是二叉树上最重要的操作之一,是二叉树上进行其它运算之基础。

二叉树的遍历方法有以下四种 :

  • 先序遍历(根左右)

        首先访问根结点,然后递归地遍历左子树,最后遍历右子树。例如对于二叉树:先序遍历的结果为:1->2->4->8->9->5->10->11->3->6->7。

  • 中序遍历(左根右)

        首先遍历左子树,然后访问根结点,最后遍历右子树。例如对于二叉树:中序遍历的结果为:8->4->9->2->10->5->11->1->6->3->7。

  • 后序遍历(左右根)

        首先遍历左子树,然后遍历右子树,最后访问根结点。例如对于二叉树:后序遍历的结果为:8->9->4->10->11->5->2->6->7->3->1。

  • 层序遍历

   自上而下,自左至右逐层访问树的结点的过程就是层序遍历。      

  它是按广度优先搜索的策略,从根结点出发,依次访问每一层上的节点。这种策略在实际应用中使用较多,如在计算机图形学中用于渲染场景图等。例如对于二叉树:层次遍历的结果为:1->2->3->4->5->6->7->8->9->10->11

下面讲解代码,前序遍历、中序遍历和后序遍历的简单递归方法不附图讲解,主要解释它们的迭代方式及层序遍历


1、前序遍历

递归方式:

定义 preorder(root) 表示当前遍历到 root 节点的答案。按照定义,我们只要首先将 root 节点的值加入答案,然后递归调用 preorder(root.left) 来遍历 root 节点的左子树,最后递归调用 preorder(root.right) 来遍历 root 节点的右子树即可,递归终止的条件为碰到空节点。

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

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

复杂度分析

时间复杂度:O(n)其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n),为递归过程中栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。

迭代方式: 

迭代的方式实现方法一的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码。

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root==null) return res;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode p = stack.pop();
            res.add(p.val);
            if(p.right!=null)
            stack.push(p.right);
            if(p.left!=null
            )stack.push(p.left);
        }
        return res;
    }
}

注释: 先将root压栈,进入 while 循环,每循环一次栈顶出栈一次并记录该结点,将其左结点右结点压栈,栈为空退出循坏。

时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。


2、中序遍历

递归方式:

定义 inorder(root) 表示当前遍历到 root 节点的答案,那么按照定义,我们只要递归调用 inorder(root.left) 来遍历 root 节点的左子树,然后将 root 节点的值加入答案,再递归调用inorder(root.right) 来遍历 root 节点的右子树即可,递归终止的条件为碰到空节点。

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);
    }
}

复杂度分析

时间复杂度:O(n),其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。

空间复杂度:O(n)。空间复杂度取决于递归的栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n) 的级别

 迭代方式:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<>();
        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            res.add(root.val);
            root = root.right;
        }
        return res;
    }
}

子循环中root遍历到root左子树最深层次过程中,依次将左结点压栈,左结点为空退出子循环,将栈顶元素出栈, root=root.right重新进入大循环既可遍历该树所有结点。

时间复杂度:O(n),其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。

空间复杂度:O(n)。空间复杂度取决于栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n) 的级别。 


3、后序遍历

递归方式

定义 postorder(root) 表示当前遍历到 root 节点的答案。按照定义,我们只要递归调用 postorder(root->left) 来遍历 root 节点的左子树,然后递归调用 postorder(root->right) 来遍历 root 节点的右子树,最后将 root 节点的值加入答案即可,递归终止的条件为碰到空节点。

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

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

复杂度分析

时间复杂度:O(n),其中 n 是二叉搜索树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n),为递归过程中栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。

迭代方式:

我们也可以用迭代的方式实现方法一的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码(为了更好的注释,代码放入块引用中)

class Solution {

    public List<Integer> postorderTraversal(TreeNode root) {

        List<Integer> res = new ArrayList<Integer>();

        if (root == null) {

            return res;

        }

        Stack <TreeNode> stack = new Stack<>();

        TreeNode prev = null;

        while (root != null || !stack.isEmpty()) {

            while (root != null) {

                stack.push(root);

                root = root.left;

            }

            root = stack.peek();//  取栈顶元素

//因为是后序遍历,当前结点有无右结点决定它是否打印

//所以取出栈顶元素,判断有无右结点

         //而打印当前结点情况分为两种,1、该结点无右结点  2、该结点的右结点已遍历

      if (root.right == null || root.right == prev) {

                res.add(root.val);

                stack.pop();

                prev = root;//用prev指向root,表示已被遍历

                root = null;

            } else 

                root = root.right;

        }

        return res;

    }

}

如上图情况,若if条件语句不判断 D.right ( K )是否被上次遍历则会陷入K出栈压栈无限循环中

复杂度分析 

时间复杂度:O(n),其中 n 是二叉搜索树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。


4、层序遍历

即逐层地,从左到右访问所有节点。

递归方式

class Solution {
    List<List<Integer>> result=new ArrayList<>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        order(root,0);
        return result;
    }
    public void order(TreeNode node,int deep)
    {
        if(node==null)return;
        deep++;
        if(result.size()<deep)
        {
            List<Integer> item=new ArrayList<>();
            result.add(item);
        }
        result.get(deep-1).add(node.val);
        order(node.left,deep);
        order(node.right,deep);
    }
}

在方法void order(TreeNode node,int deep) ,传相应的结点和二维数组下标,设置节点对应位置

加图理解: 

时间复杂度;O(n) 

迭代方式: 

代码设计重点:出栈既遍历。在结点出栈时,同时将不为空的左右结点入栈。进入子循环前,用size记录栈内元素,进入子循环后,依次将栈顶元素弹出,同时将不为空的左右结点入,size--;

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res=new ArrayList<>();
        if(root==null)
        return res;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            List<Integer> list=new ArrayList<>();
            while(size--!=0){
                TreeNode top=queue.poll();
                list.add(top.val);
               if(top.left!=null)
               queue.offer(top.left);
                if(top.right!=null)
                queue.offer(top.right);
            }
            res.add(list);
        }
        return res;
    }
}

时间复杂度;O(n)

二叉树的遍历结束

评论 113
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值