目录
LeetCode 166:填充每个节点的下一个右侧节点指针(完全二叉树)
LeetCode 117:填充每个节点的下一个右侧节点指针ii(二叉树)
二叉树02
2.1 层序遍历
在二叉树01中我们讲到了二叉树的遍历方式包括深度优先遍历(前序、中序、后序)以及广度优先遍历即层次遍历,深度优先遍历既可以用迭代法,又可以用递归法来实现,而层次遍历只能通过迭代法来实现。
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树,需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
LeetCode 102:二叉树的层序遍历
思路:两种方法,第一种递归,第二种迭代
首先用递归法,
- 参数为根节点和层级(初始值deep为0),返回二维数组resList
- 终止条件:节点为null
- 单层循环:首先判断是否为空节点,不是则在数组中新建二维数组,然后将root节点的值赋给二维数组
class Solution {
List<List<Integer>> resList = new ArrayList<>(); //二维列表
public List<List<Integer>> levelOrder(TreeNode root) {
DFS_Fun(root,0);
return resList;
}
public void DFS_Fun(TreeNode root,int deep){
if(root == null) return;
deep++;
if(resList.size()<deep){ //已存在该层列表则无需新建,没有则新建itemList
List<Integer> itemList = new ArrayList<>();
resList.add(itemList);
}
resList.get(deep-1).add(root.val);
if(root.left != null) DFS_Fun(root.left,deep);
if(root.right != null) DFS_Fun(root.right,deep);
}
}
其次常用迭代法,迭代法需要用到while循环,利用队列,思想同样是
- 参数为根节点,返回二维数组resList
- 终止条件:队列为null
- 相比于递归法将层级deep作为参数,迭代法则在while循环里通过获取队列的长度len(元素个数)来添加每一层子数组itemList的元素
- 用到了双层循环,一层用于终止条件判断和新建子数组,一层用于向子数组中添加元素
- 注意:节点用null判断。queue用isEmpty判断
class Solution {
List<List<Integer>> resList = new ArrayList<>(); //二维列表
public List<List<Integer>> levelOrder(TreeNode root) {
BFS_Fun(root);
return resList;
}
public void BFS_Fun(TreeNode root){
if(root == null) return;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
List<Integer> itemList = new ArrayList<>();
int len = que.size();
while(len>0){
TreeNode tmp = que.poll();
itemList.add(tmp.val);
if(tmp.left != null) que.offer(tmp.left);
if(tmp.right != null) que.offer(tmp.right);
len--;
}
resList.add(itemList);
}
}
}
LeetCode 107:二叉树的层序遍历(从下往上)
倒序即可,
List<List<Integer>> result = new ArrayList<>();
for (int i = list.size() - 1; i >= 0; i-- ) {
result.add(list.get(i));
}
LeetCode 199:二叉树的右视图
迭代时能否遍历到每层的最后一个元素,如果能则加入数组
将len--的while循环改为for循环,方便进行元素个数判断
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> list = new ArrayList<>();
Queue<TreeNode> que = new LinkedList<>();
if(root == null) return list;
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
for(int i = 0;i < len;i++){
TreeNode tmp = que.poll();
if(tmp.left != null) que.offer(tmp.left);
if(tmp.right != null) que.offer(tmp.right);
if(i == len-1) list.add(tmp.val);
}
}
return list;
}
}
LeetCode 637:二叉树的层平均值
就是在层序遍历的循环内求一个平均值
再盘一遍顺序:
建立数组和队列——判断根节点是否为空,添加根节点——while第一层循环判断len,第二层循环进行多样化操作,向队列中添加len个元素
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> list = new ArrayList<>(); //放结果
Queue<TreeNode> que = new LinkedList<>(); //放层级队列
if(root == null) return list;
que.offer(root); //初始化
while(!que.isEmpty()){
int len = que.size();
double sum = 0; //存在精度要求
for(int i = 0;i<len;i++){
TreeNode tmp = que.poll();
sum += tmp.val;
if(tmp.left != null) que.offer(tmp.left);
if(tmp.right != null) que.offer(tmp.right);
}
list.add(sum/len);
}
return list;
}
}
LeetCode 429:N叉树的层序遍历
相比二叉树只有left和right,N叉树有多个孩子
List<Node> children = poll.children;
if (children == null || children.size() == 0) {
continue;
}
for (Node child : children) {
if (child != null) {
que.offerLast(child);
}
}
LeetCode 515:在每个树行中找最大值
判断最大数的常见做法,max = math.max(max,node.val)
class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> list = new ArrayList<>(); //放结果
Queue<TreeNode> que = new LinkedList<>(); //放层级队列
if(root == null) return list;
que.offer(root); //初始化
while(!que.isEmpty()){
int len = que.size();
int max = Integer.MIN_VALUE;
for(int i = 0;i<len;i++){
TreeNode tmp = que.poll();
max = Math.max(max,tmp.val);
if(tmp.left != null) que.offer(tmp.left);
if(tmp.right != null) que.offer(tmp.right);
}
list.add(max);
}
return list;
}
}
LeetCode 166:填充每个节点的下一个右侧节点指针(完全二叉树)
LeetCode 117:填充每个节点的下一个右侧节点指针ii(二叉树)
思路:没差啦
class Solution {
public Node connect(Node root) {
Queue<Node> queue = new LinkedList<>();
if (root != null) {
queue.add(root);
}
while (!queue.isEmpty()) {
int size = queue.size();
Node node = null;
Node nodePre = null;
for (int i = 0; i < size; i++) {
if (i == 0) {
nodePre = queue.poll(); // 取出本层头一个节点
node = nodePre;
} else {
node = queue.poll();
nodePre.next = node; // 本层前一个节点 next 指向当前节点
nodePre = nodePre.next;
}
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
nodePre.next = null; // 本层最后一个节点 next 指向 null
}
return root;
}
}
LeetCode 104:二叉树的最大深度
思路:最大的深度就是二叉树的层数
class Solution {
public int maxDepth(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
if(root != null) que.offer(root);
int depth = 0;
while(!que.isEmpty()){
int len = que.size();
depth++;
for(int i = 0;i < len;i++){
TreeNode tmp = que.poll();
if(tmp.left != null) que.offer(tmp.left);
if(tmp.right != null) que.offer(tmp.right);
}
}
return depth;
}
}
LeetCode 111:二叉树的最小深度
思路:左右孩子都为空的时候说明到遍历的最低点了
class Solution {
public int minDepth(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
if(root != null) que.offer(root);
int depth = 0;
while(!que.isEmpty()){
int len = que.size();
depth++;
for(int i = 0;i < len;i++){
TreeNode tmp = que.poll();
if(tmp.left != null) que.offer(tmp.left);
if(tmp.right != null) que.offer(tmp.right);
if(tmp.right == null && tmp.left == null) return depth;
}
}
return depth;
}
}
2.2 LeetCode226:翻转二叉树(优先掌握递归)
思路:把每个节点的左右孩子都翻转一下,就是整体翻转
这道题目使用前序遍历、后序遍历和层序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了
1. 递归法
DFS前序遍历(中左右):先交换左右孩子节点,再反转左子树,翻转右子树
//DFS递归
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层序遍历:
//BFS
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {return null;}
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
while (!que.isEmpty()) {
int size = que.size();
while (size-- > 0) {
TreeNode node = que.poll();
swap(node);
if (node.left != null) que.offer(node.left);
if (node.right != null) que.offer(node.right);
}
}
return root;
}
public void swap(TreeNode root) {
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
}
}
2.3 LeetCode101:对称二叉树(优先掌握递归)
思路:
本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。
1. 递归法
①参数和返回值:参数是左子树节点和右子树节点(比较两颗子树),返回值类型为bool
②终止条件:
左子树为空,右子树不为空——false;
左子树不为空,右子树为空——false;
左右子树都为空——true;
左右子树都不为空且值相等——true;
不等——false
③单层递归逻辑:
比较左节点的左/右孩子和右节点的右/左孩子,都对称则返回true
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
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;
else return compare(left.left,right.right) && compare(left.right,right.left);
}
}
2. 迭代法
我知道一开始是用栈的,但这里用队列
普通队列:
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root.left);
que.offer(root.right);
while(!que.isEmpty()){ //队列就是先进先出啊别忘了
TreeNode left = que.poll();
TreeNode right = que.poll();
if(left==null && right==null) continue;
if(left==null||right==null||left.val!=right.val) return false;
//接着把left.left和right.right放一块对比
que.offer(left.left);
que.offer(right.right);
que.offer(left.right);
que.offer(right.left);
}
return true;
}
}
双头队列:还可以用deque(ArrayDeque
和 LinkedList
这两种类型的 Deque
都可以用作双端队列(双头队列))只不过用的是offerFirst和offerLast