Bootstrap

代码随想录算法训练营day15

代码随想录算法训练营day15

层序遍历专题

思路
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。

需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ARPmALk-1687871390517)(https://note.youdao.com/yws/res/89011/WEBRESOURCE3a27c0fde7df16d52b8da4ba9fc6ca74)]

二叉树的层序遍历

https://leetcode.cn/problems/binary-tree-level-order-traversal/

题目

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

思路

代码

迭代
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) return res;
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.offer(root);
        int size;
        while(!q.isEmpty()){
            List<Integer> tmp = new ArrayList<>();
            size = q.size();
            while(size-- > 0){
                TreeNode node = q.poll();
                tmp.add(node.val);
                if(node.left != null) q.offer(node.left);
                if(node.right != null) q.offer(node.right);
            }
            res.add(tmp);
        }
        return res;
    }
}
递归
class Solution {

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

    public List<List<Integer>> levelOrder(TreeNode root) {
        dfs(root,0);
        return res;
    }

    public void dfs(TreeNode root , int deep){
        if (root == null) return;
        deep++;
        if(res.size() < deep){
            res.add(new ArrayList<>());
        }
        res.get(deep-1).add(root.val);
        dfs(root.left, deep);
        dfs(root.right, deep);
    }
}

二叉树的层次遍历 II

题目

给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RWmgfpF4-1687871390518)(https://note.youdao.com/yws/res/89019/WEBRESOURCEaafc574cae000bdfe23379a147b7288e)]

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

思路

相对于102.二叉树的层序遍历,就是最后把result数组反转一下就可以了。

代码

result数组反转
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> list = new ArrayList<>();
        if (root == null) return list;
        Queue<TreeNode> nodeList = new LinkedList<>();
        nodeList.offer(root);
        int size;
        while(!nodeList.isEmpty()){
            List<Integer> tmp = new ArrayList<>();
            size = nodeList.size();
            while(size -- > 0){
                TreeNode node = nodeList.poll();
                tmp.add(node.val);
                if(node.left != null) nodeList.offer(node.left);
                if(node.right != null) nodeList.offer(node.right);
            }
            list.add(tmp);
        }

        List<List<Integer>> res = new ArrayList<>();
        for (int i = list.size() - 1; i >= 0; i-- ) {
            res.add(list.get(i));
        }
        return res;
    }
}
不需要反转(用链表)
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        // 利用链表可以进行 O(1) 头部插入, 这样最后答案不需要再反转
        LinkedList<List<Integer>> res = new LinkedList<>();
        if (root == null) return res;
        Queue<TreeNode> nodeList = new LinkedList<>();
        nodeList.offer(root);
        int size;
        while(!nodeList.isEmpty()){
            List<Integer> tmp = new ArrayList<>();
            size = nodeList.size();
            while(size -- > 0){
                TreeNode node = nodeList.poll();
                tmp.add(node.val);
                if(node.left != null) nodeList.offer(node.left);
                if(node.right != null) nodeList.offer(node.right);
            }
            res.addFirst(tmp);
        }
        return res;
    }
}

二叉树的右视图

https://leetcode.cn/problems/binary-tree-right-side-view/

题目

给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

思路

层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中,随后返回result就可以了。

代码

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root == null) return res;
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while(!q.isEmpty()){
            int size = q.size();
            for(int i = 0; i < size; i++){
                TreeNode node = q.poll();
                if(node.left != null) q.offer(node.left);
                if(node.right != null) q.offer(node.right);
                if(i == size - 1){
                    res.add(node.val);
                }
            }
        }
        return res;
    }
}

二叉树的层平均值

https://leetcode.cn/problems/average-of-levels-in-binary-tree/

题目

给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KBGdRf58-1687871390518)(https://note.youdao.com/yws/res/89042/WEBRESOURCE4a64121a46860fb4d8e79d481d3fadcc)]

输入: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] 。

思路

层序遍历的时候把一层求个总和在取一个均值

代码

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> res = new ArrayList<>();
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while(!q.isEmpty()){
            int size = q.size();
            Double sum = 0.0;
            for(int i = 0; i < size; i++){
                TreeNode node = q.poll();
                sum += node.val;
                if(node.left != null) q.offer(node.left);
                if(node.right != null) q.offer(node.right);
            }
            res.add(sum / size);
        }
        return res;
    }
}

N叉树的层序遍历

https://leetcode.cn/problems/n-ary-tree-level-order-traversal/

题目

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

例如,给定一个 3叉树 :

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DpqjPIcu-1687871390518)(https://note.youdao.com/yws/res/89053/WEBRESOURCE06c89eb997f3069d336461af48dc84d0)]

返回其层序遍历:

[ [1], [3,2,4], [5,6] ]

思路

这道题依旧是模板题,只不过一个节点有多个孩子了

代码

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) return res;
        Queue<Node> q = new LinkedList<>();
        q.offer(root);
        while(!q.isEmpty()){
            int size = q.size();
            List<Integer> tmp = new ArrayList<>();
            while(size-- > 0){
                Node node = q.poll();
                tmp.add(node.val);
                List<Node> children = node.children;
                if(children == null || children.size() == 0){
                    continue;
                }

                for (Node child : children) {
                    if (child != null) {
                        q.offer(child);
                    }
                }
            }
            res.add(tmp);
        }
        return res;
    }
}

在每个树行中找最大值

题目

您需要在二叉树的每一行中找到最大的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dEdKvU8T-1687871390519)(https://note.youdao.com/yws/res/89061/WEBRESOURCEd639831c5b1da1f90583a4ce05e4f449)]

思路

层序遍历,取每一层的最大值

代码

class Solution {
    public List<Integer> largestValues(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root == null) return res;
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while(!q.isEmpty()){
            int size = q.size();
            int max = Integer.MIN_VALUE;
            while(size-- > 0){
                TreeNode node = q.poll();
                max = Math.max(max, node.val);
                if(node.left != null) q.offer(node.left);
                if(node.right != null) q.offer(node.right);
            }
            res.add(max);
        }
        return res;
    }
}

二叉树的最大深度

https://leetcode.cn/problems/maximum-depth-of-binary-tree/

题目

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:

给定二叉树 [3,9,20,null,null,15,7],

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K9CLJJVG-1687871390519)(https://note.youdao.com/yws/res/89071/WEBRESOURCE5f5a01bc2408bfaace0a1c44b9a43ae0)]

返回它的最大深度 3 。

思路

使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。

代码

class Solution {
    public int maxDepth(TreeNode root) {
        int deep = 0;
        if (root == null) return deep;
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while(!q.isEmpty()){
            int size = q.size();
            deep++;
            while(size-- > 0){
                TreeNode node = q.poll();
                if(node.left != null) q.offer(node.left);
                if(node.right != null) q.offer(node.right);
            }
        }
        return deep;
    }
}

二叉树的最小深度

题目

相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。

思路

需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点

代码

class Solution {
    public int minDepth(TreeNode root) {
        int deep = 0;
        if (root == null) return deep;
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while(!q.isEmpty()){
            int size = q.size();
            deep++;
            while(size-- > 0){
                TreeNode node = q.poll();
                if(node.left != null) q.offer(node.left);
                if(node.right != null) q.offer(node.right);
                if(node.left == null && node.right == null){
                    return deep;
                }
            }
        }
        return deep;
    }
}

填充每个节点的下一个右侧节点指针

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/

题目

给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8zq5rRPo-1687871390519)(https://note.youdao.com/yws/res/89088/WEBRESOURCE00b2aeebc2719bb5e1e5fe53763d4720)]

思路

本题依然是层序遍历,只不过在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了

代码

class Solution {
    public Node connect(Node root) {
        if (root == null) return root;
        Queue<Node> q = new LinkedList<>();
        q.add(root);
        while(!q.isEmpty()){
            int size = q.size();
            Node node, nodePre = null;
            for(int i = 0; i < size; i++){
                if(i == 0){
                    // 获取第一层的头节点
                    nodePre = q.poll();
                    node = nodePre;
                }else{
                    node = q.poll();
                    nodePre.next = node;
                    nodePre = nodePre.next;
                }
                if(node.left != null) q.offer(node.left);
                if(node.right != null) q.offer(node.right);
            }
        }
        return root;
    }
}

填充每个节点的下一个右侧节点指针II

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node-ii/

题目

这道题目说是二叉树,但116题目说是完整二叉树,其实没有任何差别,一样的代码一样的逻辑一样的味道

思路

代码

class Solution {
    public Node connect(Node root) {
        if (root == null) return root;
        Queue<Node> q = new LinkedList<>();
        q.offer(root);
        Node node = null, nodePre = null;
        while(!q.isEmpty()){
            int size = q.size();
            for(int i = 0; i < size; i++){
                if(i == 0){
                    // 取出一层的头结点
                    nodePre = q.poll();
                    node = nodePre;
                }else{
                    node = q.poll();
                    nodePre.next = node;
                    nodePre = nodePre.next;
                }
                if(node.left != null) q.offer(node.left);
                if(node.right != null) q.offer(node.right);
            }
        }
        return root;
    }
}

翻转二叉树

题目

翻转一棵二叉树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FrWrXXUh-1687871390519)(https://note.youdao.com/yws/res/89100/WEBRESOURCE8a4964b4818a67946a2a30d16abdd2de)]

思路

前后序遍历都可以

代码

迭代(BFS)

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return root;
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while(!q.isEmpty()){
            int size = q.size();
            while(size-- > 0){
                TreeNode node = q.poll();
                swap(node);
                if(node.left != null) q.offer(node.left);
                if(node.right != null) q.offer(node.right);
            }
        }
        return root;

    }

     public void swap(TreeNode root) {
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
    }
}

递归(DFS)

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return root;
        swap(root);
        invertTree(root.left);
        invertTree(root.right);
        return root;

    }

     public void swap(TreeNode root) {
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
    }
}

拓展

递归的中序遍历是不行的,因为使用递归的中序遍历,某些节点的左右孩子会翻转两次。

如果一定要使用递归中序的方式写,也可以

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return root;
        invertTree(root.left);
        swap(root);
        invertTree(root.left); // 这里还是左孩子,因为中间翻转了
        return root;

    }

     public void swap(TreeNode root) {
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
    }
}

对称二叉树

题目

给定一个二叉树,检查它是否是镜像对称的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TRVlitvk-1687871390520)(https://note.youdao.com/yws/res/89117/WEBRESOURCEd9ae2d42883e340ec48f59eb53b2fd0e)]

思路

首先想清楚,判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点!

对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7H8HfVLv-1687871390520)(https://note.youdao.com/yws/res/89122/WEBRESOURCE5062719eb32c78b08150fe1907c68008)]

代码

递归

递归三部曲

1、确定递归函数的参数和返回值

因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。

返回值自然是bool类型。

2、确定终止条件

节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点)

  • 左节点为空,右节点不为空,不对称,return false
  • 左不为空,右为空,不对称 return false
  • 左右都为空,对称,返回true

此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:

  • 左右都不为空,比较节点数值,不相同就return false

3、确定单层递归的逻辑

此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。

  • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
  • 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
  • 如果左右都对称就返回true ,有一侧不对称就返回false 。
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null) return true;
        return compare(root.left, root.right);
    }

    // 递归返回值
    public boolean compare(TreeNode leftNode, TreeNode rightNode){
        // 递归终止条件
        if(leftNode == null && rightNode != null) return false;
        else if(leftNode != null && rightNode == null) return false;
        else if(leftNode == null && rightNode == null) return true;
        else if(leftNode.val != rightNode.val) return false;

        // 下一层
        boolean outSide = compare(leftNode.left, rightNode.right);
        boolean inSide = compare(leftNode.right, rightNode.left);
        boolean isSame = outSide && inSide;
        return isSame;
    }
}

迭代

使用队列

通过队列来判断根节点的左子树和右子树的内侧和外侧是否相等

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) return true;
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root.left);
        q.offer(root.right);
        while(!q.isEmpty()){
            TreeNode leftNode = q.poll();
            TreeNode rightNode = q.poll();

            // 如果两个都为空则相同
            if(leftNode == null && rightNode == null){
                continue;
            }

            if(leftNode == null && rightNode != null) return false;
            else if(leftNode != null && rightNode == null) return false;
            else if(leftNode.val != rightNode.val) return false;

            q.offer(leftNode.left);     // 将左子树 左节点加入队列
            q.offer(rightNode.right);   // 将右子树 右节点加入队列
            q.offer(leftNode.right);    // 将左子树 右节点加入队列
            q.offer(rightNode.left);    // 将右子树 左节点加入队列

        }
        return true;
    }
}
;