《算法通关村第六关——层次遍历》

层次遍历

对于层次遍历,我们可以用队列先进先出的特点,在出列的时候将左右子树放入队列中,这样出列的顺序就是树每一层的顺序
在这里插入图片描述

基本的层次遍历

2.0 如何确定底基层有哪些节点

我们在遍历队列时,当前队列的长度就是该层的节点,不会出现节点中出现下一层节点的情况,因为每一次循环我们会将该层的所有节点全部抛出。

2.1 层次遍历

LeetCode102 题目要求:给你一个二叉树,请你返回其按层序遍历得到的节点值。(即逐层地,从左到右访问所有节点)。
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

在这里插入图片描述

代码如下:

	public static List<List<Integer>> level102Order(TreeNode root) {
        if (root == null) {
            return new ArrayList<List<Integer>>();
        }

        List<List<Integer>> res = new ArrayList<List<Integer>>();
        LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
        //将根节点放入队列中,然后不断遍历队列
        queue.add(root);
        while (queue.size() > 0) {
            //获取当前队列的长度,这个长度相当于 当前这一层的节点个数
            int size = queue.size();
            ArrayList<Integer> tmp = new ArrayList<Integer>();
            //将队列中的元素都拿出来(也就是获取这一层的节点),放到临时list中
            //如果节点的左/右子树不为空,也放入队列中
            for (int i = 0; i < size; ++i) {
                TreeNode t = queue.remove();
                tmp.add(t.val);
                if (t.left != null) {
                    queue.add(t.left);
                }
                if (t.right != null) {
                    queue.add(t.right);
                }
            }
            //将临时list加入最终返回结果中
            res.add(tmp);
        }
        return res;
    }

这里的SIZE就是确定了该层的节点数,这样就算后面将新的子节点插入,也能限制防止多抛。

2.2 自底向上

LeetCode 107.给定一个二叉树,返回其节点值自底向上的层序遍历。(即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)。例如给定的二叉树为:
输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]

在这里插入图片描述
本题相比于基本的层次遍历,我们只需要将结果用头插法的方式插入结果即可。且通过链表结构插入的话,插入头插法的时间复杂度为O(1)。

代码如下:

	public static List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> levelOrder = new LinkedList<List<Integer>>();
        if (root == null) {
            return levelOrder;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            List<Integer> level = new ArrayList<Integer>();
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                level.add(node.val);
                TreeNode left = node.left, right = node.right;
                if (left != null) {
                    queue.offer(left);
                }
                if (right != null) {
                    queue.offer(right);
                }
            }
            levelOrder.add(0, level);//栈
        }
        return levelOrder;
    }

2.3 二叉树的锯齿形层序遍历

LeetCode103 题,要求是:给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]

在这里插入图片描述

  1. 加个判断,如果是true则正序插入输出,否则是逆序插入输出。isOrderLeft
  2. 我们需要通过双端队列存入每一层的结果 Deque levelList = new LinkedList();

代码如下:

	public static List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> ans = new LinkedList<List<Integer>>();
        if (root == null) {
            return ans;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        boolean isOrderLeft = true;
        while (!queue.isEmpty()) {
            Deque<Integer> levelList = new LinkedList<Integer>();
            int size = queue.size();
            for (int i = 0; i < size; ++i) {
                TreeNode curNode = queue.poll();
                if (isOrderLeft) {
                    levelList.offerLast(curNode.val);
                } else {
                    levelList.offerFirst(curNode.val);
                }
                if (curNode.left != null) {
                    queue.offer(curNode.left);
                }
                if (curNode.right != null) {
                    queue.offer(curNode.right);
                }
            }
            ans.add(new LinkedList<Integer>(levelList));
            isOrderLeft = !isOrderLeft;
        }
        return ans;
    }

2.4 N叉树的层次遍历

树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]

在这里插入图片描述

  1. 左右孩子变成了一个列表。所以我们由添加左右孩子 -> 对列表进行循环添加。
  2. 通过另一个队列来记录弹出后节点的子列表,不用通过SIZE去记录改成的数据。当然,通过SIZE也是一样的效果。

代码如下:

	public static List<List<Integer>> nLevelOrder(NTreeNode root) {
        List<List<Integer>> value = new ArrayList<>();
        Deque<NTreeNode> q = new ArrayDeque<>();
        if (root != null)
            q.addLast(root);
        while (!q.isEmpty()) {
            Deque<NTreeNode> next = new ArrayDeque<>();
            List<Integer> nd = new ArrayList<>();
            while (!q.isEmpty()) {
                NTreeNode cur = q.pollFirst();
                nd.add(cur.val);
                for (NTreeNode chd : cur.children) {
                    if (chd != null)
                        next.add(chd);
                }
            }
            q = next;
            value.add(nd);
        }
        return value;
    }

处理每层元素的节点

3.0 基本原理

我们先看接下来的几题题目的特征:

  1. 每个树行中寻找最大值
  2. 每个树行中寻找平均值
  3. 二叉树的右视图
  4. 最底层最左边

可以发现,是对每一层元素进行判断,修改等。所以只要在遍历每层元素的时候添加相对应的逻辑判断即可。

3.1 每个树行中寻找最大值

给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
输入: root = [1,3,2,5,3,null,9]
输出: [1,3,9]

在这里插入图片描述
只要在层次遍历的时候记录每层的最大值即可

代码如下:

	public static List<Integer> largestValues(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Deque<TreeNode> deque = new ArrayDeque<>();

        if (root != null) {
            deque.addLast(root);
        }

        while (!deque.isEmpty()) {
            int size = deque.size();
            int levelMaxNum = Integer.MIN_VALUE;
            for (int i = 0; i < size; i++) {
                TreeNode node = deque.poll();
                levelMaxNum = Math.max(node.val, levelMaxNum);
                if (node.left != null) deque.addLast(node.left);
                if (node.right != null) deque.addLast(node.right);
            }
            res.add(levelMaxNum);
        }
        return res;
    }

3.2 每个树行中寻找平均值

给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值
输入:root = [3,9,20,null,null,15,7]
输出:[3.00000,14.50000,11.00000] 解释:第 0 层的平均值为 3,第 1 层的平均值为 14.5,第 2 层的平均值为 11 。 因此返回 [3, 14.5, 11] 。

在这里插入图片描述
与上题类似,只需将最大值的判断换为取平均值即可:

代码如下:

public static List<Double> averageOfLevels(TreeNode root) {
        List<Double> res = new ArrayList<>();
        if (root == null) return res;
        Queue<TreeNode> list = new LinkedList<>();
        list.add(root);
        while (list.size() != 0) {
            int len = list.size();
            double sum = 0;
            for (int i = 0; i < len; i++) {
                TreeNode node = list.poll();
                sum += node.val;
                if (node.left != null) list.add(node.left);
                if (node.right != null) list.add(node.right);
            }
            res.add(sum / len);
        }
        return res;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林深时见璐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值