Day14 二叉树 part2
- BFS & DFS
- 十道本质一样的层序遍历
- Leetcode102 Binary Tree Level Order Traversal
- Leetcode107 Binary Tree Level Order Traversal II
- Leetcode199 Binary Tree Right Side View
- Leetcode637 Average of Levels in Binary Tree
- Leetcode429 N-ary Tree Level Order Traversal
- Leetcode515 Find Largest Value in Each Tree Row
- Leetcode116 Populating Next Right Pointers in Each Node
- Leetcode117 Populating Next Right Pointers in Each Node II
- Leetcode104 二叉树的最大深度
- Leetcode111 Minimum Depth of Binary Tree
- Leetcode226 Invert Binary Tree
- Leetcode101 Symmetric Tree
BFS & DFS
广度优先: 队列先进先出,符合一层一层遍历的逻辑, 迭代
深度优先: 栈先进后出适合模拟深度优先遍历也就是递归的逻辑 前中后序既可以递归也可迭代
十道本质一样的层序遍历
Leetcode102 Binary Tree Level Order Traversal
Method1 BFS 借助队列
Notice: 层序遍历的过程和取出queue里面的值是两个步骤, 不应搞混
- 先把root塞入queue,
!queue.isEmpty()
用来遍历当前这一层. 在这一层里, 把queue里面的root取出并把新的左右子树放入该queue (此时该queue是为了后面的第二层). root被poll后, 此时原queue size = 0
再进入新的queue的循环 - 开始第二层的层序遍历, 依然用
!queue.isEmpty()
遍历当前这一层, 后续同理
class Solution {
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
bfs(root);
return result;
}
public void bfs(TreeNode root){
if (root == null) return;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while (!queue.isEmpty()){
List<Integer> list = new ArrayList<>();
int size = queue.size();
while (size > 0){
TreeNode node = queue.poll();
list.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
size--;
}
result.add(list);
}
}
}
Method2 DFS 递归
class Solution {
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
dfs(root,0);
return result;
}
// Method2 DFS
public void dfs(TreeNode root, int deep){
if (root == null) return;
deep++;
// 此处用来判定需不需要添加新的List<Integer>
if (result.size() < deep){
List<Integer> list = new ArrayList<>();
result.add(list);
}
result.get(deep-1).add(root.val);
dfs(root.left, deep);
dfs(root.right, deep);
}
}
Leetcode107 Binary Tree Level Order Traversal II
题目链接
与上题的区别在于从下往上输出了, Collections.reverse(list)
即可解决
class Solution {
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
bfs(root);
// dfs(root,0);
return result;
}
// Method 1: BFS 借助队列
public void bfs(TreeNode root){
if (root == null) return;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while (!queue.isEmpty()){
List<Integer> list = new ArrayList<>();
int size = queue.size();
while (size > 0){
TreeNode node = queue.poll();
list.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
size--;
}
result.add(list);
}
Collections.reverse(result);
}
}
Leetcode199 Binary Tree Right Side View
题目链接 二叉树的右视图
本题一开始写的时候误以为是只考虑右子树, 这是不对的. 而是从右往左看二叉树
class Solution {
List<Integer> result = new ArrayList<>();
public List<Integer> rightSideView(TreeNode root) {
bfs(root);
return result;
}
public void bfs(TreeNode root){
if (root == null) return;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(root);
while(!queue.isEmpty()){
int size = queue.size();
// 循环queue
for (int i = 0; i < size; i++){
TreeNode tmp = queue.poll();
// 优先放入右子树
if (tmp.right != null) queue.add(tmp.right);
if (tmp.left != null) queue.add(tmp.left);
// 如果左右子树同时存在, 那么只放第一个值
if (i == 0) result.add(tmp.val);
}
}
}
}
Leetcode637 Average of Levels in Binary Tree
题目链接 算每一层的平均值
同102, 得出每一层的所有值后求平均, 每一层的 queue.size
就是分母
class Solution {
List<Double> result = new ArrayList<>();
public List<Double> averageOfLevels(TreeNode root) {
dfs(root);
return result;
}
public void dfs(TreeNode root){
if (root == null) return;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while(!queue.isEmpty()){
double sum = 0.0;
double size = queue.size(), num = size;
while(size > 0){
TreeNode tmp = queue.poll();
sum += tmp.val;
if (tmp.left != null) queue.offer(tmp.left);
if (tmp.right != null) queue.offer(tmp.right);
size--;
}
double average = sum / num;
result.add(average);
}
}
}
Leetcode429 N-ary Tree Level Order Traversal
题目链接 二叉树改为任意个数
TreeNode改为Node, 同理. 循环子树即可
class Solution {
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> levelOrder(Node root) {
bfs(root);
return result;
}
public void bfs(Node root){
if (root == null) return;
Queue<Node> queue = new ArrayDeque<>();
queue.offer(root);
while(!queue.isEmpty()){
List<Integer> med = new ArrayList<>();
int size = queue.size();
while (size > 0){
Node tmp = queue.poll();
med.add(tmp.val);
if (tmp.children != null){
for (Node x: tmp.children){
queue.offer(x);
}
}
size--;
}
result.add(med);
}
}
}
Leetcode515 Find Largest Value in Each Tree Row
题目链接 找每一行最大值
class Solution {
List<Integer> result = new ArrayList<>();
public List<Integer> largestValues(TreeNode root) {
bfs(root);
return result;
}
public void bfs(TreeNode root){
if (root == null) return;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while(!queue.isEmpty()){
int size = queue.size();
int maximum = Integer.MIN_VALUE;
while(size > 0){
TreeNode tmp = queue.poll();
if (tmp.val > maximum) maximum = tmp.val;
if (tmp.left != null) queue.offer(tmp.left);
if (tmp.right != null) queue.offer(tmp.right);
size--;
}
result.add(maximum);
}
}
}
Leetcode116 Populating Next Right Pointers in Each Node
Method 1
我复制了一个新的copy queue, 来确定tmp要指向哪个node
class Solution {
public Node connect(Node root) {
return bfs(root);
}
public Node bfs(Node root){
if (root == null) return null;
Queue<Node> queue = new ArrayDeque<>();
queue.offer(root);
while(!queue.isEmpty()){
int size = queue.size();
Queue<Node> copy = new ArrayDeque<>(queue);
for (int i = 0; i < size; i++){
Node tmp = queue.poll();
copy.poll();
tmp.next = copy.peek();
if (tmp.left != null) queue.offer(tmp.left);
if (tmp.right != null) queue.offer(tmp.right);
}
}
return root;
}
}
Method2
代码随想录提供了一个新的思路, 即先poll最优先值, 如果queue里还有, 再poll后面的值, 确定tmp.next
可以不用管null, 因为什么都不做node就指向null
class Solution {
public Node connect(Node root) {
return bfs(root);
}
public Node bfs(Node root){
if (root == null) return null;
Queue<Node> queue = new ArrayDeque<>();
queue.offer(root);
while (!queue.isEmpty()){
int size = queue.size();
Node tmp = queue.poll();
if (tmp.left != null) queue.offer(tmp.left);
if (tmp.right != null) queue.offer(tmp.right);
for (int i = 1; i < size; i++){
Node nex = queue.poll();
if (nex.left != null) queue.offer(nex.left);
if (nex.right != null) queue.offer(nex.right);
tmp.next = nex;
tmp = tmp.next;
}
}
return root;
}
}
Leetcode117 Populating Next Right Pointers in Each Node II
题目链接 填充每个节点的下一个右侧节点指针II
同116
Leetcode104 二叉树的最大深度
题目链接 二叉树的最大深度
Method1 BFS
广度优先搜索就是遍历每一层, 每一层depth++, 很合理
class Solution {
public int maxDepth(TreeNode root) {
return bfs(root);
}
public int bfs(TreeNode root){
if (root == null) return 0;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
int depth = 0;
while (!queue.isEmpty()){
int size = queue.size();
depth++;
while (size > 0){
TreeNode tmp = queue.poll();
if (tmp.left != null) queue.offer(tmp.left);
if (tmp.right != null) queue.offer(tmp.right);
size--;
}
}
return depth;
}
}
但是在这用BFS很慢
Method2 DFS
backtrack = dfs + 剪枝
class Solution {
int depth = 0;
int maxDepth = 0;
public int maxDepth(TreeNode root) {
traverse(root);
return maxDepth;
}
public void traverse(TreeNode root){
if (root == null) return;
depth++;
if (root.left == null && root.right == null){
maxDepth = Math.max(maxDepth, depth);
}
traverse(root.left);
traverse(root.right);
depth--;
}
}
Leetcode111 Minimum Depth of Binary Tree
题目链接 二叉树的最小深度
Method1 BFS
Method2 DFS
class Solution {
int depth = 0;
int minDepth = Integer.MAX_VALUE;
public int minDepth(TreeNode root) {
if (root == null){
return 0;
}
dfs(root);
return minDepth;
}
public void dfs(TreeNode root){
// 终止条件
if (root == null) return;
depth++;
// 对子树操作
dfs(root.left);
if (root.left == null && root.right == null){
minDepth = Math.min(depth, minDepth);
}
dfs(root.right);
// 回溯
depth--;
}
}
Leetcode226 Invert Binary Tree
题目链接 翻转二叉树
Method1 迭代法
Method1.1 BFS
class Solution {
public TreeNode invertTree(TreeNode root){
bfs(root);
return root;
}
public void bfs(TreeNode root){
if (root == null) return;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while(!queue.isEmpty()){
int size = queue.size();
while (size > 0){
TreeNode tmp = queue.poll();
if (tmp.left != null) queue.offer(tmp.left);
if (tmp.right != null) queue.offer(tmp.right);
// 左右交换
TreeNode x = tmp.left;
tmp.left = tmp.right;
tmp.right = x;
size--;
}
}
}
}
Method1.2 DFS深度优先遍历
Method2 递归
DFS: 前序遍历
- 确定递归函数的参数和返回值
invertTree(TreeNode root)
- 确定终止条件
if (root == null) return null;
- 确定单层递归逻辑 左右子树交换
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
invertTree(root.right);
return root;
}
Leetcode101 Symmetric Tree
一开始做本题时想单纯比较左右子树, 这是错误的, 实际除了root以外都无法单纯比较左右子树
1
/ \
2 2
/ \
3 3
在第二层2的左右子树显然不一样.
Method1: 递归
该题真正需要比较的是根节点的左右子树, 即子树的内侧和外侧是否相等, 在递归时要同时遍历两棵树
Notice: 本题的遍历方式只能是后序遍历, 我们需要在比较完左右子树之后再向上返回值, 外侧相同和内侧相同必须同时true以后, 才能告诉父类
如果用左中右, 父节点显然无法知道它的一端子树是否是对称的
后序: 收集孩子的信息, 向上一层返回值
- 确定函数类型和返回对象: 根节点的两个子树是否可以翻转 -> 比较左右两个树
boolean compare(TreeNode left, TreeNode right)
- 确定终止条件 每一次递归完后返回什么: 比较的不是左右孩子!! 左节点和右节点
if ((left == null && right != null) || (left != null && right == null)) return false;
else if (left == null && right == null) return true;
else if (left.val != right.val) return false;
- 确定单层递归逻辑: 先比较外侧, 再比较内层, 最后返回到父结点
public boolean isSymmetric(TreeNode root) {
boolean result = compare(root.left, root.right);
return result;
}
public boolean compare(TreeNode left, TreeNode right){
// 确定终止条件
if ((left == null && right != null) || (left != null && right == null)) return false;
else if (left == null && right == null) return true;
else if (left.val != right.val) return false;
boolean a = compare(left.left, right.right);
boolean b = compare(left.right, right.left);
return a&&b;
}
Method2 迭代 (非bfs)
只能用Linkedlist<>构建queue, LinkedList允许element为null, ArrayDeque若子树出现null会报错, 加不进去
public boolean isSymmetric(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root.left);
queue.offer(root.right);
while (!queue.isEmpty()){
TreeNode left = queue.poll();
TreeNode right = queue.poll();
if (left == null && right == null) continue;
if (left == null || right == null || left.val != right.val) {
return false;
}
queue.offer(left.left);
queue.offer(right.right);
queue.offer(left.right);
queue.offer(right.left);
}
return true;
}