这个系列主要是记录我刷题的过程。重点是每一类型题解法的循序渐进,按着这个顺序基本每一题都能做出来。而不是某一题的解法,所以适合打算大量刷题的人参考。二叉树相关的刷题顺序是参考 代码随想录,感兴趣的可以到该公众号上下载对应pdf。
下面的12道题需要些二叉树遍历的基础,题目不难。
226. 翻转二叉树
难度简单
翻转一棵二叉树。
示例:
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
输出:
4
/ \
7 2
/ \ / \
9 6 3 1
备注: 这个问题是受到 Max Howell 的 原问题 启发的 :
谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。
思路
这可是一道难倒了 Max Howell的算法!!!!
配合笔记一,其实不难想到,使用遍历并配合swap就能解出。遍历其实还是递归写起来更舒适,下面使用前序遍历。代码很简单:
/**
* 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 TreeNode invertTree(TreeNode root) {
if(root==null)return root;
swapRootChildren(root);
invertTree(root.left);
invertTree(root.right);
return root;
}
//交换当前结点下的两个子节点的位置
private void swapRootChildren(TreeNode root){
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
}
}
看了下性能还行:
101. 对称二叉树
难度简单1240
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3]
是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3]
则不是镜像对称的:
1
/ \
2 2
\ \
3 3
思路
所谓的镜像对称,就是左边的结点的值等于右边节点的值。如果所有的都满足则是对称二叉树。
/**
* 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 boolean isSymmetric(TreeNode root) {
return check(root,root);
}
//从root开始,分两个方向挨个向下遍历并比较
public boolean check(TreeNode l,TreeNode r){
//需要主要null节点也是需要对称,且左右节点都为null,也可以退出递归了
if(l==null&&r==null) return true;
if(l==null||r==null) return false;
//注意这里递归的元素 需要对称(镜像) l.right--r.left l.left--r.right
return l.val==r.val&&check(l.left,r.right)&&check(l.right,r.left);
}
}
104. 二叉树的最大深度
难度简单792
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7]
,
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
思路
本题通过深度优先遍历,每到一个叶子结点的时候记录下当前的深度。最后获取最大的深度:
/**
* 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 {
private int maxD;
public int maxDepth(TreeNode root) {
if(root==null) return 0;
preoder(root,0);
return maxD;
}
private void preoder(TreeNode root,int deep){
if(root==null){
maxD=Math.max(deep,maxD);
return;
}
deep++;
preoder(root.left,deep);
preoder(root.right,deep);
//这里其实是回溯了
deep--;
}
}
其实这道题使用 **层序遍历,**更符合正常逻辑,因为层序遍历就是一层层往下走,走到最后一层肯定就是最大深度了:
/**
* 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 int maxDepth(TreeNode root) {
Queue<TreeNode> queue = new ArrayDeque();
if(root!=null){
queue.add(root);
}
int depth =0;
while(!queue.isEmpty()){
int n =queue.size();
//其实走到whilie里面一次 正好是增加一层
depth++;
for(int i = 0 ;i<n; i++){
TreeNode node = queue.poll();
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
}
return depth;
}
}
559. N 叉树的最大深度
难度简单148
给定一个 N 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。
示例 1:
输入:root = [1,null,3,2,4,null,5,6]
输出:3
示例 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]
输出:5
提示:
- 树的深度不会超过
1000
。 - 树的节点数目位于
[0, 104]
之间。
思路
这一题用104. 二叉树的最大深度的思路就行,不过需要注意N叉树的叶子结点的判断条件是children为空。还用本题用层序遍历的思路会超时:
/*
// 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 {
private int maxD;
public int maxDepth(Node root) {
if(root==null) return 0;
preoder(root,0);
return maxD;
}
private void preoder(Node root,int deep){
deep++;
//注意叶子结点的判断条件
if(root.children.isEmpty()){
maxD=Math.max(deep,maxD);
return;
}
for(Node child: root.children){
preoder(child,deep);
}
deep--;
}
}
111. 二叉树的最小深度
难度简单453
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
**说明:**叶子节点是指没有子节点的节点。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:2
示例 2:
输入:root = [2,null,3,null,4,null,5,null,6]
输出:5
提示:
- 树中节点数的范围在
[0, 105]
内 -1000 <= Node.val <= 1000
思路
这题如果直接使用104. 二叉树的最大深度](leetcode-cn.com/problems/ma…** 。什么是叶子结点呢?没有子节点的叫作叶子节点,即left和right都为null
/**
* 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 {
private int minD=Integer.MAX_VALUE;
public int minDepth(TreeNode root) {
if(root==null) return 0;
preoder(root,0);
return minD;
}
private void preoder(TreeNode root,int deep){
if(root==null){
//这不能认为是一条路径
return;
}
deep++;
//这才是到了叶子节点
if(root.left==null&&root.right==null){
minD=Math.min(deep,minD);
return;
}
preoder(root.left,deep);
preoder(root.right,deep);
deep--;
}
}
222. 完全二叉树的节点个数
难度中等434
给你一棵 完全二叉树 的根节点 root
,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h
层,则该层包含 1~ 2h
个节点。
示例 1:
输入:root = [1,2,3,4,5,6]
输出:6
示例 2:
输入:root = []
输出:0
示例 3:
输入:root = [1]
输出:1
提示:
- 树中节点的数目范围是
[0, 5 * 104]
0 <= Node.val <= 5 * 104
- 题目数据保证输入的树是 完全二叉树
思路
这道题可以按普通二叉树来解:
/**
* 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 int countNodes(TreeNode root) {
if(root==null) return 0;
return 1+ countNodes(root.left)+countNodes(root.right);
}
}
这道题还可以按照 完全二叉树的特性来解。完全二叉树中肯定有一部分是 满二叉树组成。而满二叉树的节点个数只需要知道树深度(H)即可:2<<H-1(2的H次方):
/**
* 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 int countNodes(TreeNode root) {
if(root==null) return 0;
TreeNode left =root.left;
TreeNode right =root.right;
int leftHeight =0;
while(left!=null){
leftHeight++;
left=left.left;
}
int rightHeight = 0;
while(right!=null){
rightHeight++;
right=right.right;
}
//左右高度相同的是满二叉树
if(leftHeight==rightHeight){
return (2<<leftHeight)-1;
}
//不是满二叉树的部分还是上面那种一个一个加的方法
return countNodes(root.left)+countNodes(root.right)+1;
}
}
110. 平衡二叉树
难度简单593
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:true
示例 2:
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例 3:
输入:root = []
输出:true
提示:
- 树中的节点数在范围
[0, 5000]
内 -104 <= Node.val <= 104
思路
需要判断每个非叶子节点左右的深度差是否小于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 boolean isBalanced(TreeNode root) {
if(root==null){
return true;
}
return Math.abs(getNodeDepth(root.left)-getNodeDepth(root.right))<=1
&&isBalanced(root.left)
&&isBalanced(root.right);
}
//获取当前节点的深度(注意深度是指最大的)
private int getNodeDepth(TreeNode root){
if(root==null){
return 0;
}else{
return Math.max(getNodeDepth(root.left),getNodeDepth(root.right))+1;
}
}
}
257. 二叉树的所有路径
难度简单442
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
输入:
1
/ \
2 3
\
5
输出: ["1->2->5", "1->3"]
解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3
思路
这个就是深度优先,递归:
/**
* 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<String> binaryTreePaths(TreeNode root) {
List<String> result = new ArrayList();
dfs(root,result,new ArrayList<Integer>());
return result;
}
private void dfs(TreeNode root,List<String> result,List<Integer> path){
if(root==null){
return;
}
path.add(root.val);
//叶子节点
if(root.left==null&&root.right==null){
int length = path.size();
StringBuffer sb = new StringBuffer();
for(int i = 0;i<length;i++){
sb.append(path.get(i));
if(i!=length-1){
sb.append("->");
}
}
result.add(sb.toString());
//回溯
path.remove(length-1);
return;
}
dfs(root.left,result,path);
dfs(root.right,result,path);
//回溯
path.remove(path.size()-1);
}
}
404. 左叶子之和
难度简单280
计算给定二叉树的所有左叶子之和。
示例:
3
/ \
9 20
/ \
15 7
在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
思路
首先什么是 左叶子:左节点+叶子节点
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int sum =0;
public int sumOfLeftLeaves(TreeNode root) {
sum(root);
return sum;
}
private void sum(TreeNode root){
if(root==null) return;
if(root.left!=null){
//左节点 是叶子节点
if(root.left.left==null&&root.left.right==null){
sum =sum+root.left.val;
}
}
sum(root.left);
sum(root.right);
}
}
513. 找树左下角的值
难度中等148
给定一个二叉树,在树的最后一行找到最左边的值。
示例 1:
输入:
2
/ \
1 3
输出:
1
示例 2:
输入:
1
/ \
2 3
/ / \
4 5 6
/
7
输出:
7
注意: 您可以假设树(即给定的根节点)不为 NULL。
思路
直接记录层序遍历最左边的即可:
/**
* 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 {
private int bottomLeftValue =0;
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new ArrayDeque();
queue.add(root);
while(!queue.isEmpty()){
int n = queue.size();
for(int i =0;i<n;i++){
TreeNode node = queue.poll();
if(i==0){
bottomLeftValue=node.val;
}
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
}
return bottomLeftValue;
}
}
112. 路径总和
难度简单512
给你二叉树的根节点 root
和一个表示目标和的整数 targetSum
,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
示例 2:
输入:root = [1,2,3], targetSum = 5
输出:false
示例 3:
输入:root = [1,2], targetSum = 0
输出:false
提示:
- 树中节点的数目在范围
[0, 5000]
内 -1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000
思路
这道题其实用回溯也能做,需要注意的是叶子节点的定义
/**
* 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 boolean hasPathSum(TreeNode root, int targetSum) {
if(root==null) return false;
if(root.left==null&&root.right==null){
return targetSum==root.val;
}
return hasPathSum(root.left,targetSum-root.val)||hasPathSum(root.right,targetSum-root.val);
}
}
![image-20210219225427781](/Users/frc/Library/Application Support/typora-user-images/image-20210219225427781.png)
113. 路径总和 II
难度中等425
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22
,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
思路
由于本题需要记录路径,所以在递归的基础上有回溯:
/**
* 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<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<List<Integer>> result = new ArrayList();
if(root==null) return result;
backtracking(root,targetSum,new ArrayList<Integer>(),result);
return result;
}
private void backtracking(TreeNode root,int targetSum,List<Integer> path,List<List<Integer>> result){
if(root==null){
return;
}
path.add(root.val);
if(root.left==null&&root.right==null){
//当前root是叶子节点
if(targetSum==root.val){
result.add(new ArrayList(path));
}
//回溯
path.remove(path.size()-1);
return ;
}else{
backtracking(root.left,targetSum-root.val,path,result);
backtracking(root.right,targetSum-root.val,path,result);
//回溯
path.remove(path.size()-1);
}
}
}