层序遍历
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。
需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
而这种层序遍历方式就是图论中的广度优先遍历,只不过应用在二叉树上。
借用队列结构实现,代码较为简单:
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int count = queue.size();
List<Integer> res = new ArrayList<Integer>();
while(count != 0) {
TreeNode top = queue.poll();
if(top.left != null) queue.offer(top.left);
if(top.right != null) queue.offer(top.right);
res.add(top.val);
count--;
}
result.add(res);
}
return result;
}
}
相对于102.二叉树的层序遍历,就是最后把result数组反转一下就可以了。代码如下:
lass Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int count = queue.size();
List<Integer> res = new ArrayList<Integer>();
while(count != 0){
TreeNode top = queue.poll();
if(top.left != null) queue.offer(top.left);
if(top.right != null) queue.offer(top.right);
res.add(top.val);
count--;
}
result.add(res);
}
Collections.reverse(result);
return result;
}
}
最开始没理解到位题目的意思,写的思路是只将结点的右孩子入队列,提交时样例没过。看了题解才知道人家的意思是求每一层最后一个结点,修改后代码如下:
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
if(root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int count = queue.size();
for(int i=0; i<count; i++){
TreeNode top = queue.poll();
if(i == (count - 1)) result.add(top.val);
if(top.left != null) queue.offer(top.left);
if(top.right != null) queue.offer(top.right);
}
}
return result;
}
}
LeetCode 637.二叉树的层平均值
本题就是层序遍历的时候把一层求个总和在取一个均值。注意类型的定义,代码如下:
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> result = new ArrayList<Double>();
if(root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int count = queue.size();
double sum = 0;
for(int i=0; i<count; i++) {
TreeNode top = queue.poll();
sum += top.val;
if(top.left != null) queue.offer(top.left);
if(top.right != null) queue.offer(top.right);
}
result.add(sum / count);
}
return result;
}
}
LeetCode 429.N叉树的层序遍历
这道题依旧是模板题,只不过一个节点有多个孩子了。代码如下:
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(root == null) return result;
Queue<Node> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int count = queue.size();
List<Integer> res = new ArrayList<Integer>();
while(count != 0) {
Node top = queue.poll();
res.add(top.val);
count--;
if(top.children == null || top.children.size() == 0) continue;
for(Node child:top.children) {
if(child != null) {
queue.offer(child);
}
}
}
result.add(res);
}
return result;
}
}
LeetCode 515.在每个树行中找最大值
层序遍历,取每一层的最大值。代码如下:
class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
if(root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int count = queue.size();
int max = Integer.MIN_VALUE;;
while(count != 0) {
TreeNode top = queue.poll();
if(top.left != null) queue.offer(top.left);
if(top.right != null) queue.offer(top.right);
if(top.val > max) {
max = top.val;
}
count--;
}
result.add(max);
}
return result;
}
}
LeetCode 116.填充每个节点的下一个右侧节点指针
本题依然是层序遍历,只不过在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了。代码如下:
class Solution {
public Node connect(Node root) {
if(root == null) return root;
Queue<Node> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int count = queue.size();
Node front = queue.poll();
if(front.left != null) queue.offer(front.left);
if(front.right != null) queue.offer(front.right);
while(count != 1) {
Node top = queue.poll();
if(top.left != null) {
queue.offer(top.left);
}
if(top.right != null) queue.offer(top.right);
front.next = top;
front = top;
count--;
}
}
return root;
}
}
LeetCode 117.填充每个节点的下一个右侧节点指针II
这道题目说是二叉树,但116题目说是完整二叉树,其实没有任何差别,一样的代码一样的逻辑一样的味道。
LeetCode 104.二叉树的最大深度
最大的深度就是二叉树的层数,采用层序遍历然后记录层数返回即可。代码如下:
class Solution {
public int maxDepth(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if(root == null) return 0;
queue.offer(root);
int result = 0;
while(!queue.isEmpty()) {
int count = queue.size();
result++;
while(count != 0) {
TreeNode top = queue.poll();
if(top.left != null) queue.offer(top.left);
if(top.right != null) queue.offer(top.right);
count--;
}
}
return result;
}
}
LeetCode 111.二叉树的最小深度
层序遍历时,判断一下当前结点是否为叶子结点,如果是则返回当前的深度,不是则继续遍历。代码如下:
class Solution {
public int minDepth(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if(root == null) return 0;
queue.offer(root);
int result = 0;
while(!queue.isEmpty()) {
int count = queue.size();
result++;
while(count != 0) {
TreeNode top = queue.poll();
if(top.left == null && top.right == null) return result;
if(top.left != null) queue.offer(top.left);
if(top.right != null) queue.offer(top.right);
count--;
}
}
return result;
}
}
总结
二叉树的层序遍历,就是图论中的广度优先搜索在二叉树中的应用,需要借助队列来实现,上面10个题基本是用一份代码模版来做的,还比较简单。
LeetCode 226.翻转二叉树
递归遍历时,可采用前序和后序,中序遍历代码有点绕。题目还是比较简单的,前序遍历代码如下:
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;
}
}
LeetCode 101. 对称二叉树
什么是对称二叉树呢?若左子树和右子树可以相互翻转,则称该二叉树为对称二叉树。
此题采用后序遍历,为啥用后序遍历呢?因为需要收集左结点和右结点的信息返回给父节点。
最后需要注意一下递归处理返回值的情况不要漏掉,代码如下:
class Solution {
public boolean isSymmetric(TreeNode root) {
return compare(root.left, root.right);
}
public boolean compare(TreeNode left, TreeNode right) {
if(left == null && right != null) {
return false;
} else if(left != null && right == null) {
return false;
}else if(left == null && right == null) {
return true;
}else if(left.val != right.val) {
return false;
}
boolean outside = compare(left.left, right.right);
boolean inside = compare(left.right, right.left);
return outside && inside;
}
}