这个系列主要是记录我刷题的过程。重点是每一类型题解法的循序渐进,按着这个顺序基本每一题都能做出来。而不是某一题的解法,所以适合打算大量刷题的人参考。二叉树相关的刷题顺序是参考 代码随想录,感兴趣的可以到该公众号上下载对应pdf。
遍历
二叉树的遍历可以分为:深度优先遍历和广度优先遍历。
常说的:前序遍历、中序遍历、后续遍历就是深度优先的方式。
而深度优先的方式又有:递归和 迭代两种。
递归: 代码简洁但空间负责度高
迭代: 虽然空间会省一点,但是使用了Stack,所以时间上会差一些
深度优先
深度优先遍历,其实就是一个树枝一个树枝的遍历。
144. 二叉树的前序遍历
难度中等513
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
示例 4:
输入:root = [1,2]
输出:[1,2]
示例 5:
输入:root = [1,null,2]
输出:[1,2]
提示:
- 树中节点数目在范围
[0, 100]内 -100 <= Node.val <= 100
递归写法
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result=new ArrayList();
preorder(root,result);
return result;
}
private void preorder(TreeNode root,List<Integer> result){
if(root==null) return;
result.add(root.val);
preorder(root.left,result);
preorder(root.right,result);
}
}
看上面的时间效率虽然很好,但是空间复杂度很差
迭代写法
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result= new ArrayList();
preorder(root,result);
return result;
}
private void preorder(TreeNode root,List<Integer> result){
Stack<TreeNode> stack =new Stack();
while(root!=null||!stack.isEmpty()){
while(root!=null){
result.add(root.val);
stack.push(root);
root=root.left;
}
TreeNode cur=stack.pop();
root=cur.right;
}
}
}
145. 二叉树的后序遍历
难度中等521
给定一个二叉树,返回它的 后序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
递归写法
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList();
postorder(root,result);
return result;
}
private void postorder(TreeNode root,List<Integer> result){
if(root==null)return;
postorder(root.left,result);
postorder(root.right,result);
result.add(root.val);
}
}
迭代写法
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<TreeNode>();
while(root!=null||!stack.isEmpty()){
while(root!=null){
result.add(root.val);
stack.push(root);
root = root.right;
}
TreeNode cur = stack.pop();
root = cur.left;
}
Collections.reverse(result);
return result;
}
}
94. 二叉树的中序遍历
难度中等858
给定一个二叉树的根节点 root ,返回它的 中序 遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[1,3,2]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
示例 4:
输入:root = [1,2]
输出:[2,1]
示例 5:
输入:root = [1,null,2]
输出:[1,2]
提示:
- 树中节点数目在范围
[0, 100]内 -100 <= Node.val <= 100
递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result= new ArrayList();
inorder(root,result);
return result;
}
private void inorder(TreeNode root,List<Integer> result){
if(root==null)return ;
inorder(root.left,result);
result.add(root.val);
inorder(root.right,result);
}
}
迭代
中序遍历的迭代写法跟前两种有点不同,主要是记录参数的时候。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList();
Stack<TreeNode> stack = new Stack();
while(root!=null||!stack.isEmpty()){
while(root!=null){
stack.push(root);
root = root.left;
}
root = stack.pop();
result.add(root.val);
root = root.right;
}
return result;
}
}
广度优先
在二叉树中广度优先,其实就是一层一层的遍历,即 层序遍历。
广度优先借助了 队列的先进先出的特性。模板如下:
void bfs(TreeNode root) {
Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(root);//根节点
while (!queue.isEmpty()) {
TreeNode node = queue.poll(); // 取出最先进入的Node
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
}
102. 二叉树的层序遍历
难度中等773
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
示例:
二叉树:[3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其层序遍历结果:
[
[3],
[9,20],
[15,7]
]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result =new ArrayList();
//双端队列
Queue<TreeNode> queue = new ArrayDeque();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
//记录每一层的数据
List<Integer> level = new ArrayList();
//当前层的宽度
int N = queue.size();
for(int i = 0;i < N;i++){
//取出最前面的Node
TreeNode node =queue.poll();
level.add(node.val);
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
result.add(level);
}
return result;
}
}
107. 二叉树的层序遍历 II
难度简单405
给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其自底向上的层序遍历为:
[
[15,7],
[9,20],
[3]
]
本题与上一道的区别在于,需要将上一道最后返回的result 倒序即可:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> result = new ArrayList();
Queue<TreeNode> deque = new ArrayDeque();
if(root!=null){
deque.add(root);
}
while(!deque.isEmpty()){
List<Integer> level =new ArrayList();
int n =deque.size();
for(int i = 0; i < n; i++){
TreeNode node =deque.poll();
level.add(node.val);
if(node.left!=null){
deque.add(node.left);
}
if(node.right!=null){
deque.add(node.right);
}
}
//这样先遍历出来的上层,排序在后面。
result.add(0,level);
}
return result;
}
}
199. 二叉树的右视图
难度中等405
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例:
输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:
1 <---
/ \
2 3 <---
\ \
5 4 <---
这一题与之前的区别是:本题只需要记录每一行的最后一个元素即可:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> result = new ArrayList();
Queue<TreeNode> deQueue = new ArrayDeque();
if(root!=null){
deQueue.add(root);
}
while(!deQueue.isEmpty()){
int n = deQueue.size();
for(int i = 0; i < n; i++){
TreeNode node = deQueue.poll();
if(i==n-1){
//只需要记录这一行最后一个即可
result.add(node.val);
}
if(node.left!=null){
deQueue.add(node.left);
}
if(node.right!=null){
deQueue.add(node.right);
}
}
}
return result;
}
}
637. 二叉树的层平均值
难度简单236
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
示例 1:
输入:
3
/ \
9 20
/ \
15 7
输出:[3, 14.5, 11]
解释:
第 0 层的平均值是 3 , 第1层是 14.5 , 第2层是 11 。因此返回 [3, 14.5, 11] 。
提示:
- 节点值的范围在32位有符号整数范围内。
本地与上面不同的在于获取每行的总和然后获取平均数
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> result = new ArrayList();
Queue<TreeNode> deQueue = new ArrayDeque();
if(root!=null){
deQueue.add(root);
}
while(!deQueue.isEmpty()){
int n = deQueue.size();
//注意这里使用Double类型
Double levelSum =0.0;
for(int i = 0; i < n; i++){
TreeNode node = deQueue.poll();
//加
levelSum=levelSum+node.val;
if(node.left!=null){
deQueue.add(node.left);
}
if(node.right!=null){
deQueue.add(node.right);
}
}
result.add(levelSum/n);
}
return result;
}
}
429. N 叉树的层序遍历
难度中等130
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。
树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
示例 1:
输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]
示例 2:
输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]
提示:
- 树的高度不会超过
1000 - 树的节点总数在
[0, 10^4]之间
本题与 102. 二叉树的层序遍历的区别在于当前每个Node下可能有N个Node。
/*
// 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) {
List<List<Integer>> result =new ArrayList();
//双端队列
Queue<Node> queue = new ArrayDeque();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
//记录每一层的数据
List<Integer> level = new ArrayList();
//当前层的宽度
int N = queue.size();
for(int i = 0;i < N;i++){
//取出最前面的Node
Node node =queue.poll();
level.add(node.val);
//N个结点
for(Node child : node.children){
queue.add(child);
}
}
result.add(level);
}
return result;
}
}
515. 在每个树行中找最大值
难度中等124
您需要在二叉树的每一行中找到最大的值。
示例:
输入:
1
/ \
3 2
/ \ \
5 3 9
输出: [1, 3, 9]
本题与637. 二叉树的层平均值基本一样,一个求最大值,一个求平均值
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> result = new ArrayList();
Queue<TreeNode> deQueue = new ArrayDeque();
if(root!=null){
deQueue.add(root);
}
while(!deQueue.isEmpty()){
int n = deQueue.size();
//当前层最大值,由于Node可能是负数,所以这里不能是0
Integer levelMax = Integer.MIN_VALUE;
for(int i = 0; i < n; i++){
TreeNode node = deQueue.poll();
//获取较大的值
levelMax=Math.max(node.val,levelMax);
if(node.left!=null){
deQueue.add(node.left);
}
if(node.right!=null){
deQueue.add(node.right);
}
}
result.add(levelMax);
}
return result;
}
}
116. 填充每个节点的下一个右侧节点指针
难度中等395
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
进阶:
-
你只能使用常量级额外空间。
-
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
示例:
输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。
提示:
- 树中节点的数量少于
4096 -1000 <= node.val <= 1000
/*
// 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;
}
};
*/
class Solution {
public Node connect(Node root) {
//这里需要记录根节点
Node connectedNode=root;
Queue<Node> queue = new ArrayDeque();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
//当前层的宽度
int N = queue.size();
for(int i = 0;i < N;i++){
//取出最前面的Node
Node node=queue.poll();
//非每行的最后一个元素,
if(i<N-1){
//这里使用peek(),只取值不弹出
node.next=queue.peek();
}
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
}
return connectedNode;
}
}
117. 填充每个节点的下一个右侧节点指针 II
这道题跟上一题解法没啥区别。虽然说上一题是完全二叉树,但是上一题的解法根本不需要考虑是否为完全二叉树。