前言
二叉树
- (opens new window)
- 107.二叉树的层次遍历II
- (opens new window)
- 199.二叉树的右视图
- (opens new window)
- 637.二叉树的层平均值
- (opens new window)
- 429.N叉树的层序遍历
- (opens new window)
- 515.在每个树行中找最大值
- (opens new window)
- 116.填充每个节点的下一个右侧节点指针
- (opens new window)
- 117.填充每个节点的下一个右侧节点指针II
- (opens new window)
- 104.二叉树的最大深度
- (opens new window)
- 111.二叉树的最小深度
一、层序遍历
层序遍历,一层层的遍历,图论中的广度优先遍历同样采用这样的方法。队列先进先出,符合一层层便利的逻辑。
在题目102.二叉树的层序遍历中,刚开始暴露出了两个问题,其一,queue的函数混淆;其二,没有搞清楚怎样逐层的添加。
有问题的代码:
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
public List<List<Integer>> res = new ArrayList<>();
return res;
}
public void cengci(TreeNode root){
if(root == null){return;}
Queue<TreeNode> queue = new Queue<>();//
List<Integer> res1 = new ArrayList<>();
TreeNode node = root;
queue.push(node);//
int size = queue.size();
while(size != 0){
node = queue.peek();//
queue.pop();
if(node.left != null){
queue.push(node.left);
}
if(node.right != null){
queue.push(node.right);
}
size--;
}
}
}
正确的:
class Solution {
public List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
com(root);
return res;
}
public void com(TreeNode root){
if(root == null) return;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
int size = queue.size();
List<Integer> li = new ArrayList<Integer>();
while(size>0){
TreeNode node = queue.poll();
li.add(node.val);
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
size-- ;
}
res.add(li);
}
}
}
重点在于反转:
- Collections.reverse(res);
- for(int i = res.size()-1;i>=0;i--){
result.add(res.get(i));
}
开始想窄了,以为只要右侧即可,忘了从右侧看可以包括左侧!
//最开始错误的代码:
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> li = new ArrayList<Integer>();
if(root == null) return li;
li.add(root.val);
while(root.right != null){
li.add(root.right.val);
root = root.right;
}
return li;
}
}
明确方法poll,它用于从队列中获取并删除队列的头部元素。
本题只需要每一行中最后的那个元素:
class Solution {
public List<Integer> rightSideView(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<Integer> res = new ArrayList<>();
queue.offer(root);
if(root == null) return res;
while(!queue.isEmpty()){
int size = queue.size();
while(size > 0){
TreeNode node = queue.poll();
// queue.pop(node);
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
if(size == 1){
res.add(node.val);
}
size--;
}
}
return res;
}
}
开始认为需要单独处理,仍然选用的List<List<>>,后来发现直接在while循环中就可以做到,而且最后直接用sum/size2,非常精妙的一句,每一次循环的size恰好如此,如果选用size,会有错误:java.lang.RuntimeException: Infinite or NaN,除数为0;
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> res = new ArrayList<>();
if(root == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
int size = queue.size();
int size2 = size;
Double sum = 0.0;
while(size > 0){
TreeNode node = queue.poll();
sum += node.val;
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
size--;
}
res.add(sum/size2);
}
return res;
}
}
需要注意的,多叉树的class和二叉树的不同:
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) {
List<List<Integer>> list =new ArrayList<>();
Deque<Node> que = new LinkedList<>();
if(root == null) return list;
que.offerLast(root);
while(!que.isEmpty()){
int levelSize = que.size();
List<Integer> levelList = new ArrayList<>();
for(int i =0;i < levelSize ;i++){
Node poll = que.pollFirst();
levelList.add(poll.val);
List<Node> children =poll.children;
if(children == null || children.size() == 0){
continue;
}
for(Node child : children){
if(child != null){
que.offerLast(child);
}
}
}
list.add(levelList);
}
return list;
}
}
二、226.翻转二叉树
思路可以分为递归和非递归(迭代),递归分为前中后序,迭代分为深度优先和广度优先。
本题中递归法适合前后序,中序不太容易想到,因为先左孩子交换孩子,再根交换孩子(做完后,右孩子已经变成了原来的左孩子),再右孩子交换孩子(此时其实是对原来的左孩子做交换)。
递归:
参数和返回条件;终止条件;单层递归的逻辑;
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;
}
}
BFS:
//BFS
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {return null;}
ArrayDeque<TreeNode> deque = new ArrayDeque<>();
deque.offer(root);
while (!deque.isEmpty()) {
int size = deque.size();
while (size-- > 0) {
TreeNode node = deque.poll();
swap(node);
if (node.left != null) deque.offer(node.left);
if (node.right != null) deque.offer(node.right);
}
}
return root;
}
public void swap(TreeNode root) {
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
}
}
三、101.对称二叉树
四星,需要回顾;
思路,递归和非递归两种,但是需要理解的是为什么用后序,前序和中序为什么不可以;
迭代法这里没有写明,下次补上。
public boolean isSymmetric1(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;
}
总结
二叉树果然不太擅长,一写就废。