常见“队列+宽搜“相关题目

 找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程(ಥ_ಥ)-CSDN博客

所属专栏: 优选算法专题

目录

429.N叉树的层序遍历

103.二叉树的锯齿形层序遍历

662.二叉树最大宽度

515.在每个树行中找最大值


429.N叉树的层序遍历

题目:

给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。

树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。

示例 1:

输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]

示例 2:

输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]

提示:

  • 树的高度不会超过 1000
  • 树的节点总数在 [0, 10^4] 之间

思路:层序遍历的大体思路,我们在前面学习二叉树的时候,就已经知道了。创建一个队列,将当前层的所有结点入队,然后再一个一个的出队并统计在数组中,在出队的过程中,拿到对应节点的所有子节点,一直重复上述过程,直至队列为空即可。 

代码实现:

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        if (root == null) {
            return new ArrayList<>();
        }
        List<List<Integer>> ret = new ArrayList<>();
        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size(); // 记录每一层的结点个数
            List<Integer> tmp= new ArrayList<>();
            while (size-- != 0) { // 循环size次,将节点的子节点全部入队
                Node cur = queue.poll();
                tmp.add(cur.val);
                int n = cur.children.size();
                for (int i = 0; i < n; i++) {
                    queue.offer(cur.children.remove(0));
                }
            }
            ret.add(tmp);
        }
        return ret;
    }
}

103.二叉树的锯齿形层序遍历

题目:

给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000] 内
  • -100 <= Node.val <= 100

思路:如果先不关注锯齿的话,本质上就是一个二叉树的层序遍历,只需要使用队列将头节点入队,然后求出队列中元素的个数(当前层节点的个数),来决定接下来需要对多少个节点进行入队左孩子右孩子的次数。将基本的层序遍历完成之后,我们只需要在原本的基础上,将拿到的结点序列进行反转即可。可以使用一个int类型的变量,来记录当前层是否需要逆序,从第二层开始就需要逆序了(根结点为第一层),接下来是第四层... 只需要不断实现变换即可。

代码实现:

class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        if (root == null) {
            return new ArrayList<>();
        }
        int flag = -1; // 记录需要逆序的层
        List<List<Integer>> ret = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            List<Integer> tmp = new ArrayList<>();
            int size = queue.size();
            while (size-- > 0) { // 循环size次
                TreeNode cur = queue.poll();
                tmp.add(cur.val);
                // 如果左孩子存在,就入队
                if (cur.left != null) {
                    queue.offer(cur.left);
                }
                // 如果右孩子存在,就入队
                if (cur.right != null) {
                    queue.offer(cur.right);
                }
            }
            // 判断是否需要逆序
            if (flag > 0) {
                Collections.reverse(tmp);
            }
            flag = -flag; // 更新逆序参数
            ret.add(tmp);
        }
        return ret;
    }
}

662.二叉树最大宽度

题目:

给你一棵二叉树的根节点 root ,返回树的 最大宽度 。

树的 最大宽度 是所有层中最大的 宽度 。

每一层的 宽度 被定义为该层最左和最右的非空节点(即,两个端点)之间的长度。将这个二叉树视作与满二叉树结构相同,两端点间会出现一些延伸到这一层的 null 节点,这些 null 节点也计入长度。

题目数据保证答案将会在  32 位 带符号整数范围内。

示例 1:

输入:root = [1,3,2,5,3,null,9]
输出:4
解释:最大宽度出现在树的第 3 层,宽度为 4 (5,3,null,9) 。

示例 2:

输入:root = [1,3,2,5,null,null,9,6,null,7]
输出:7
解释:最大宽度出现在树的第 4 层,宽度为 7 (6,null,null,null,null,null,7) 。

示例 3:

输入:root = [1,3,2,5]
输出:2
解释:最大宽度出现在树的第 2 层,宽度为 2 (3,2) 。

提示:

  • 树中节点的数目范围是 [1, 3000]
  • -100 <= Node.val <= 100

思路:虽然题目说是把二叉树看成满二叉树的形式来处理,但在我个人看来,将二叉树看作是完全二叉树来处理的方法是最好的,因为在题目给出的第3个示例中,如果是按照满二叉树的处理方式,应该是8才对,但结果是7,这刚好符合完全二叉树。其实这里也可以看出来一个规律:每一层的宽度就是该层最左侧的结点到最右侧的结点数之差 + 1。利用这个性质,我们就可以来解决本道题了。给二叉树的每个结点都赋予一个编号(下标),这样最终在计算宽度之时,只需将编号相减再+1即可。最终我们只需要层序遍历,然后求出每一层的宽度,得到最大值即可。

代码实现:

class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        // 使用List来模拟队列的先进先出
        // 为什么要模拟?最终在计算宽度时,是需要每一层最左侧的结点和最右侧的结点。
        // 左侧可以直接poll,右侧拿到比较麻烦
        List<Pair<TreeNode, Integer>> queue = new ArrayList<>();
        int ret = 0;
        queue.add(new Pair<>(root, 1)); // 把头结点加入队列
        while (!queue.isEmpty()) {
            // 先计算本层的宽度和原来的宽度的最大值并更新
            ret = Math.max(ret, queue.get(queue.size() - 1).getValue() - queue.get(0).getValue() + 1);
            List<Pair<TreeNode, Integer>> tmp = new ArrayList<>(); // 避免去头删
            // 将本层节点的下一层的结点入队
            for (Pair<TreeNode, Integer> pair : queue) {
                // 先拿到 本层节点 和 下标
                TreeNode t = pair.getKey();
                Integer i = pair.getValue();
                // 判断是否存在左右孩子
                if (t.left != null) {
                    // 左孩子入队
                    tmp.add(new Pair<>(t.left, i * 2));
                }
                if (t.right != null) {
                    // 右孩子入队
                    tmp.add(new Pair<>(t.right, i * 2 + 1));
                }
            }
            // 更新队列
            queue = tmp;
        }
        return ret;
    }
}

注意: 

1、Pair 是一个通用的数据结构,通常用于存储一对相关的值,在 Java 中,Pair 类通常来自于外部库(如 Apache Commons Lang 或 JavaFX)。和Map有点类似,但是Map存储的是多对值,而Pair只能存储一对值。以下是对Pair的简单实现(仅供参考):

/**
 * 简单实现Pair类
 * @param <K> Pair类中的key
 * @param <V> Pair类中的value
 */
class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public void setValue(V value) {
        this.value = value;
    }
}

2、上述使用List模拟的队列,可以去手动头删,也可以直接在每一层中new一个新的List对象。

3、 注意题目中给的结点数最大是3000,但是编号可能会出现溢出的情况。如下图所示:

退一万步来说,即使溢出了,我们也可以使用long,甚至是BigInteger。

515.在每个树行中找最大值

题目:

给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。

示例1:

输入: root = [1,3,2,5,3,null,9]
输出: [1,3,9]

示例2:

输入: root = [1,2,3]
输出: [1,3]

提示:

  • 二叉树的节点个数的范围是 [0,10^4]
  • -2^31 <= Node.val <= 2^31 - 1

思路:本题比较简单,只需要层序遍历,然后拿到本层val的最大值即可。

代码实现:

class Solution {
    public List<Integer> largestValues(TreeNode root) {
        if (root == null) {
            return new ArrayList<>();
        }
        List<Integer> ret = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            // 拿到本层节点的最大值,并将下一层的结点入队
            int size = queue.size(), max = Integer.MIN_VALUE; // 要设置成最小值
            while (size-- > 0) {
                TreeNode t = queue.poll();
                max = Math.max(max, t.val); // 更新最大值
                // 将当前结点的左右孩子结点入队
                if (t.left != null) {
                    queue.offer(t.left);
                }
                if (t.right != null) {
                    queue.offer(t.right);
                }
            }
            ret.add(max);
        }
        return ret;
    }
}

好啦!本期 常见“队列+宽搜“相关题目 的刷题之旅 就到此结束啦!我们下一期再一起学习吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我要学编程(ಥ_ಥ)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值