第六章 二叉树part02 层序遍历 10 101.对称二叉树

第十五天| 第六章 二叉树part02 层序遍历 10 101.对称二叉树

一、层序遍历

学会层序遍历,可以怒刷十题!!!

1.1 102. 二叉树的层序遍历

  • 题目链接:https://leetcode.cn/problems/binary-tree-level-order-traversal/
  • 思路:
    • 层序遍历提供两种思路解决:(DFS —> 递归、BFS —> 迭代)
      • 在昨天的前序、中序和后序遍历中,使用的算法是图论中的深度优先算法(Deep First Search, DFS)
      • 层序遍历使用的是广度优先算法(Breath First Search, BFS),可以用DFS的思路解决(递归)
1.1.1 解法一:DFS —> 递归

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fV9acHQd-1692801864338)(层级遍历的DFS(递归)解法.png)]

按照递归三部曲写代码:

  • (1)确定递归函数的参数和返回值;

    • 参数:需要将二叉树的根节点root参入函数,还需要设定一个层数deep用于衡量什么时候需要向二维列表中增添一维列表,还需要一个最终的结果返回列表resultList
  • (2)确定终止条件;

    • 终止条件:如果root == null,return。
  • (3)确定单层递归的逻辑。

    • 单层的递归逻辑,(注意这里的deep是否++,要看你传入的参数,把握不变量即可),接下来就是操作resultList的逻辑,分为两种情况:
      • 第一种,如果resultList.size() < deep,需要构建一个一维列表item放入resultList中,因为要保证每一层都对应一个一维列表。
      • 第二种,如果resultList.size() >= deep,说明resultList构建完成,只需要向里面填值。首先要做的是定位到第n层的一维列表,再add当前节点的val。resultList.get(dep - 1).add(root.val);
    • 接下来,就是递归处理当前节点的左右子树。

最终代码为:

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> resultList = new ArrayList<List<Integer>>();
        level(root, 0, resultList);
        return resultList;
    }

    public void level(TreeNode root, Integer deep, List<List<Integer>> resultList) {
        if (root == null) return;
        deep++;
        if (resultList.size() < deep) {
            List<Integer> result = new ArrayList<>();
            resultList.add(result);
        }
        resultList.get(deep - 1).add(root.val);
        level(root.left, deep, resultList);
        level(root.right, deep, resultList);
    }
}
1.1.2 解法二:BFS —> 迭代

迭代的过程,是模拟一个队列。

  • 思路:将每一层的节点入队,并记录每一层的size。对于每一层的逻辑是,让这一层的队首元素出列,记录队首元素的val,同时如果该节点的左右子节点不为空,就让他们入队。直到这一层的元素遍历完,即size==0,结束这一层的遍历。开启新的一轮,重复上面的操作。

  • 终止的条件是什么呢?如果队列为空了,说明所有的元素都遍历了一次,此时结束。

  • 代码:

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

1.2 107. 二叉树的层次遍历II

  • 题目链接:https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/
  • 题目介绍:给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
    • 示例:
输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]
  • 思路:将上面的列表反过来输出即可
  • 代码:
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        Deque<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> resultList = new ArrayList<List<Integer>>();
        if (root != null) {
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> result = new ArrayList<>();
            while (size-- > 0) {
                TreeNode tempNode = queue.poll();
                result.add(tempNode.val);
                if (tempNode.left != null) {
                    queue.offer(tempNode.left);
                }
                if (tempNode.right != null) {
                    queue.offer(tempNode.right);
                }
            }
            resultList.add(result);
        }
        List<List<Integer>> newResultList = new ArrayList<List<Integer>>();
        for (int i = resultList.size() - 1; i >= 0; i--) {
            newResultList.add(resultList.get(i));
        }
        return newResultList;
    }
}

1.3 199. 二叉树的右视图

  • 题目链接:https://leetcode.cn/problems/binary-tree-right-side-view/

  • 题目介绍:给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zXvnttiR-1692801864339)(右视图.png)]

    • 输入: [1,2,3,null,5,null,4]
      输出: [1,3,4]
      
  • 代码:

  • 第一种:这个代码和前面完全一样,就是把所有的节点层序遍历,然后再去每一层的最后一个结点的值。

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        Deque<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> resultList = new ArrayList<List<Integer>>();
        if (root != null) {
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> result = new ArrayList<>();
            while (size-- > 0) {
                TreeNode tempNode = queue.poll();
                result.add(tempNode.val);
                if (tempNode.left != null) {
                    queue.offer(tempNode.left);
                }
                if (tempNode.right != null) {
                    queue.offer(tempNode.right);
                }
            }
            resultList.add(result);
        }
        List<Integer> newResult = new ArrayList<>();
        for (int i = 0; i < resultList.size(); i++) {
            int size = resultList.get(i).size();
            newResult.add(resultList.get(i).get(size - 1));
        }
        return newResult;
    }
}
  • 第二种:层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中,随后返回result就可以了。
class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        Deque<TreeNode> queue = new LinkedList<>();
        List<Integer> result = new ArrayList<>();
        if (root != null) {
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            int size = queue.size();
            while (size-- > 0) {
                TreeNode tempNode = queue.poll();
                if (size == 0) {
                    result.add(tempNode.val);
                }
                if (tempNode.left != null) {
                    queue.offer(tempNode.left);
                }
                if (tempNode.right != null) {
                    queue.offer(tempNode.right);
                }
            }
        }
        return result;
    }
}

1.4 637. 二叉树的层平均值

  • 题目链接:https://leetcode.cn/problems/average-of-levels-in-binary-tree/
  • 题目介绍:给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。
  • 代码:
class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        Deque<TreeNode> queue = new LinkedList<>();
        List<Double> result = new ArrayList<>();
        if (root != null) {
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            int levelSize = queue.size();
            double levelSum = 0.0;
            for (int i = 0; i < levelSize; i++) {
                TreeNode tempNode = queue.poll();
                levelSum += tempNode.val;
                if (tempNode.left != null) {
                    queue.offer(tempNode.left);
                }
                if (tempNode.right != null) {
                    queue.offer(tempNode.right);
                }
            }
            result.add(levelSum / levelSize); 
        }
        return result;
    }
}
  • 需要注意的关键点是:
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4TwxLQlm-1692801864340)(二叉树层平均值代码注意点.png)]
    • 这里要用for循环代替while,千万不能使用while,因为我们要记录下来levelSize的值。

1.5 429. N叉树的层序遍历

  • 题目链接:https://leetcode.cn/problems/n-ary-tree-level-order-traversal/

  • 题目介绍:

    • 给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。
    • 树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5dPpSNbE-1692801864340)(N叉树.png)]
  • 思路:

    • 思路和之前一样,只要注意给定的N叉树的定义就能做出来

    • // Definition for a Node.
      class Node {
          public int val;
          public List<Node> children;
      
          public Node() {}
      
          public Node(int _val) {
              val = _val;
          }
      
          public Node(int _val, List<Node> _children) {
              val = _val;
              children = _children;
          }
      };
      
  • 代码:

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        // 还是需要一个队列
        Deque<Node> queue = new LinkedList<>();
        // 还需要一个返回值
        List<List<Integer>> resultList = new ArrayList<List<Integer>>();
        if (root != null) {
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> result = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                Node tempNode = queue.poll();
                result.add(tempNode.val);
                if (tempNode.children != null) {
                    for (Node node : tempNode.children) {
                        queue.offer(node);
                    }
                }
            }
            resultList.add(result);
        }
        return resultList;
    }
}

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

  • 题目链接:https://leetcode.cn/problems/find-largest-value-in-each-tree-row/
  • 题目描述:给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
  • 思路:
    • 在每一层的循环外设定一个最大值,最大值是每一层队首元素的值,出队的时候和最大值比。如果比它大,就重新赋值。最后,把最大值add。
  • 代码:
class Solution {
    public List<Integer> largestValues(TreeNode root) {
        // 还是需要一个队列
        Deque<TreeNode> queue = new LinkedList<>();
        // 还是需要一个结果集
        List<Integer> result = new ArrayList<>();
        if (root != null) {
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            int size = queue.size();
            int maxValue = queue.peek().val;
            while (size-- > 0) {
                TreeNode tempNode = queue.poll();
                if (tempNode.val > maxValue) {
                    maxValue = tempNode.val;
                }
                if (tempNode.left != null) {
                    queue.offer(tempNode.left);
                }
                if (tempNode.right != null) {
                    queue.offer(tempNode.right);
                }
            }
            result.add(maxValue);
        }
        return result;
    }
}

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

  • 题目链接: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-FhXztV0X-1692801864341)(填充每个节点的下一个右侧节点指针116.png)]

  • 思路:

    • 首先观察LeetCode给出的Node定义

      • // Definition for a Node.
        class Node {
            public int val;
            public Node left;
            public Node right;
            public Node next;
        
            public Node() {}
            
            public Node(int _val) {
                val = _val;
            }
        
            public Node(int _val, Node _left, Node _right, Node _next) {
                val = _val;
                left = _left;
                right = _right;
                next = _next;
            }
        };
        
    • 思路就是:每次遍历到tempNode的时候填充它的next属性,判断一下是不是最后一个,最后一个next指针设为null,其余设为poll之后队列的首。

  • 代码:

class Solution {
    public Node connect(Node root) {
        // 每次遍历到tempNode的时候填充它的右节点,判断一下是不是最后一个,最后一个next指针设为null
        // 还是需要一个队列
        Deque<Node> queue = new LinkedList<>();
        if (root != null) {
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            int size = queue.size();
            while (size-- > 0) {
                Node tempNode = queue.poll();
                if (size == 0) {
                    tempNode.next = null;
                } else {
                    tempNode.next = queue.peek();
                }
                if (tempNode.left != null) {
                    queue.offer(tempNode.left);
                }
                if (tempNode.right != null) {
                    queue.offer(tempNode.right);
                }
            }
        }
        return root;
    }
}

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

  • 题目链接: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

  • 代码:(和上题一样)

    • class Solution {
          public Node connect(Node root) {
              // 每次遍历到tempNode的时候填充它的右节点,判断一下是不是最后一个,最后一个next指针设为null
              // 还是需要一个队列
              Deque<Node> queue = new LinkedList<>();
              if (root != null) {
                  queue.offer(root);
              }
              while (!queue.isEmpty()) {
                  int size = queue.size();
                  while (size-- > 0) {
                      Node tempNode = queue.poll();
                      if (size == 0) {
                          tempNode.next = null;
                      } else {
                          tempNode.next = queue.peek();
                      }
                      if (tempNode.left != null) {
                          queue.offer(tempNode.left);
                      }
                      if (tempNode.right != null) {
                          queue.offer(tempNode.right);
                      }
                  }
              }
              return root;
          }
      }
      

1.9 104. 二叉树的最大深度

  • 题目链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree/

  • 题目介绍:

    • 给定一个二叉树 root ,返回其最大深度。
    • 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
  • 思路:

    • 第一种:采用递归DFS的思路,resultList的大小就是二叉树最大的深度。
    • 第二种:采用迭代BFS的思路,内循环的次数就是二叉树的最大深度。
  • 代码:

    • 第一种:DFS

      • class Solution {
            public int maxDepth(TreeNode root) {
                // 采用DFS试试
                List<List<Integer>> resultList = new ArrayList<List<Integer>>();
                travel(root, 0, resultList);
                return resultList.size();
            }
        
            public void travel (TreeNode root, int deep, List<List<Integer>> resultList) {
                if (root == null) return;
                deep++;
                if (resultList.size() < deep) {
                    List<Integer> item = new ArrayList<>();
                    resultList.add(item);
                }
                travel(root.left, deep, resultList);
                travel(root.right, deep, resultList);
            }
        }
        
    • 第二种:BFS

      • class Solution {
            public int maxDepth(TreeNode root) {
                Deque<TreeNode> queue = new LinkedList<>();
                int depth = 0;
                if (root != null) {
                    queue.offer(root);
                }
                while (!queue.isEmpty()) {
                    int size = queue.size();
                    while (size-- > 0) {
                        TreeNode tempNode = queue.poll();
                        if (tempNode.left != null) {
                            queue.offer(tempNode.left);
                        }
                        if (tempNode.right != null) {
                            queue.offer(tempNode.right);
                        }
                    }
                    depth++;
                }
                return depth;
            }   
        }
        

1.10 111. 二叉树的最小深度

  • 题目链接:https://leetcode.cn/problems/minimum-depth-of-binary-tree/
  • 题目介绍:
    • 给定一个二叉树,找出其最小深度。
    • 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
    • **说明:**叶子节点是指没有子节点的节点。
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1b5q5XN0-1692801864341)(二叉树最小深度.png)]
  • 思路:
    • 找到最近的叶子节点的位置,然后返回当前的所在的depth。
  • 代码:
class Solution {
    public int minDepth(TreeNode root) {
        Deque<TreeNode> queue = new LinkedList<>();
        int depth = 0;
        if (root != null) {
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            int size = queue.size();
            while (size-- > 0) {
                TreeNode tempNode = queue.poll();
                if (tempNode.left != null) {
                    queue.offer(tempNode.left);
                }
                if (tempNode.right != null) {
                    queue.offer(tempNode.right);
                }
                if (tempNode.left == null && tempNode.right == null) {
                    depth++;
                    return depth;
                }
            }
            depth++;
        }
        return depth;
    }
}

二、226.翻转二叉树

  • 题目链接:https://leetcode.cn/problems/invert-binary-tree/

  • 题目描述:给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UwNT1jq9-1692801864341)(翻转二叉树.png)]
  • 思路:

    • 可以发现想要翻转它,其实就把每一个节点的左右孩子交换一下就可以了。
    • 关键在于遍历顺序,前中后序应该选哪一种遍历顺序?还是选择层序遍历呢?
    • 答案是:前序、后序和层序都可以,唯独中序不可以
      • 为什么中序不可以呢?
      • 因为中序在交换的时候,左子树已经处理完了。如果在左子树处理完再交换,然后再去处理右子树,这个时候的右子树已经交换过了,即原来的左子树。最终的效果如下图:
      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IOJKqMeN-1692801864342)(中序遍历翻转最终失败的结果.png)]
    • 前序后序可以分别使用DFS(递归)、BFS(迭代)
    • 也可以采用层序。
  • 代码:

    • (1)DFS
    // 前序遍历递归翻转
    class Solution {
        public TreeNode invertTree(TreeNode root) {
            if (root == null) return null;
            swapChildren(root);
            invertTree(root.left);
            invertTree(root.right);
            return root;
        }
    
        private void swapChildren(TreeNode root) {
            TreeNode tmp = root.left;
            root.left = root.right;
            root.right = tmp;
        }
    }
    
    // 后序遍历递归翻转
    class Solution {
        public TreeNode invertTree(TreeNode root) {
            if (root == null) return null;
            invertTree(root.left);
            invertTree(root.right);
            swapChildren(root);
            return root;
        }
    
        private void swapChildren(TreeNode root) {
            TreeNode tmp = root.left;
            root.left = root.right;
            root.right = tmp;
        }
    }
    
    • (2)BFS
    class Solution {
        public TreeNode invertTree(TreeNode root) {
            Deque<TreeNode> stack = new LinkedList<>();
            TreeNode pop = null;
            TreeNode cur = root;
            while (cur != null || !stack.isEmpty()) {
                if (cur != null) {
                    stack.push(cur);
                    // 前序遍历迭代翻转
                    // swapChildren(cur);
                    cur = cur.left;
                } else {
                    TreeNode peek = stack.peek();
                    if (peek.right == null) {
                        pop = stack.pop();
                        // 后序遍历迭代翻转
                        swapChildren(pop);
                    } else if (peek.right == pop) {
                        pop = stack.pop();
                        // 后序遍历迭代翻转
                        swapChildren(pop);
                    } else {
                        cur = peek.right;
                    }
                }
            }
            return root;
        }
    
        private void swapChildren(TreeNode root) {
            TreeNode tmp = root.left;
            root.left = root.right;
            root.right = tmp;
        }
    }
    
    • (3)层序遍历
    class Solution {
        public TreeNode invertTree(TreeNode root) {
            Deque<TreeNode> queue = new LinkedList<>();
            if (root != null) {
                queue.offer(root);
            }
            while (!queue.isEmpty()) {
                int size = queue.size();
                while (size-- > 0) {
                    TreeNode tempNode = queue.poll();
                    swapChildren(tempNode);
                    if (tempNode.left != null) {
                        queue.offer(tempNode.left);
                    }
                    if (tempNode.right != null) {
                        queue.offer(tempNode.right);
                    }
                }
            }
            return root;
        }
    
        private void swapChildren(TreeNode root) {
            TreeNode tmp = root.left;
            root.left = root.right;
            root.right = tmp;
        }
    }
    

三、101.对称二叉树

  • 题目链接:https://leetcode.cn/problems/symmetric-tree/

  • 题目描述:给你一个二叉树的根节点 root , 检查它是否轴对称。

    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-acQTOPY9-1692801864342)(对称二叉树.png)]
  • 思路:

    • 首先想清楚,判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点!
    • 而是左右子树
      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XRTWJ2f1-1692801864342)(对称二叉树比较的是什么.png)]
    • 即什么情况下判断这个二叉树是一个对称二叉树?左子树的左节点和右子树的右节点相等,左子树的右节点和右子树的左节点相等。
    • 这里的遍历顺序是什么呢?很明显,我们需要左节点和右节点的值,再去确定是否接着递归,所以是左中右的顺序,即后序遍历。
    • 接下来我们要明确什么情况下递归会return false,什么情况下return true。
      • return false的情况有三种:左空,右不空;左不空,右空;左右不空,但值不相等;
      • return true的情况有两种:左空,右也空;左的左节点等于右的右节点,并且左的右节点等于右的左节点
      • 按照上面的思路编写代码
  • 代码:

    class Solution {
        public boolean isSymmetric(TreeNode root) {
            return compare(root.left, root.right);
        }
    
        private boolean compare(TreeNode left, TreeNode right) {
    
            if (left == null && right != null) {
                return false;
            }
            if (left != null && right == null) {
                return false;
            }
    
            if (left == null && right == null) {
                return true;
            }
            if (left.val != right.val) {
                return false;
            }
            // 比较外侧
            boolean compareOutside = compare(left.left, right.right);
            // 比较内侧
            boolean compareInside = compare(left.right, right.left);
            return compareOutside && compareInside;
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值