Leetcode树刷题总结

最小高度树

题目说明:

给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一棵高度最小的二叉搜索树。

示例:
给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0 
     / \ 
   -3   9 
   /   / 
 -10  5 

解法一:递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/

在这道题中,left和right并不代表左右结点的值,而是相应节点在数组nums中的位置,这个在编写递归开始前的判断是很重要的。

class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
//最小高度树的实现,在于找到中间节点来作为整棵树的根节点,这样构建的二叉树高度差是最小的。
if(nums.length==0){
return null;
}
return CreateMinitree(nums,0,nums.length-1);
}

public TreeNode CreateMinitree(int nums[],int left,int right){
//需要在递归的开始处增加必要的判断,防止数组越界和外节点
if(left>=right||right>nums.length||left<0){
return null;
}

//插入中间值作为父节点,随后插入左右子树
int mid = (left+right)/2;
TreeNode n = new TreeNode(nums[mid]);
n.left = CreateMinitree(nums,left,mid-1);
n.right = CreateMinitree(nums,mid+1,right);
return n;
}
}

二叉树的深度

题目说明:

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

例如:

给定二叉树 [3,9,20,null,null,15,7],

1
2
3
4
5
  3
/ \
9 20
/ \
15 7

解法一:递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}else{
int maxLeftHeight = maxDepth(root.left);
int maxRightHeight = maxDepth(root.right);
return Math.max(maxLeftHeight,maxRightHeight)+1;
}

}
}

剑指offer27.二叉树的镜像/翻转二叉树

题目说明:

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

例如输入:

解法一:DFS递归

  • 根据二叉树镜像的定义,考虑递归遍历(dfs)二叉树,交换每个节点的左 / 右子节点,即可生成二叉树的镜像。

递归解析:

  • **终止条件 **:当节点 root 为空时(即越过叶节点),则返回 null ;
  • 递推工作
    • 初始化节点 tmp ,用于暂存 root 的左子节点;
    • 开启递归右子节点mirrorTree(root,right),并将返回值作为root的左子节点。
    • 开启递归右子节点mirrorTree(root,left),并将返回值作为root的右子节点。
  • 返回值: 返回新构建的树的root结点。

Q:为何需要暂存root的左子节点?

A:在递归完成root的右子节点后,此时root.left的值已经发生改变,此时再进行递归左子树就会出现问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root==null){
return null;
}
//在暂时存储左子树的时候,一定要注意传入的是root.left,即左子树的第一个节点。不要再new TreeNode了;
TreeNode temp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(temp);
return root;

//或者是这样
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
root.left = right;
root.right = left;
return root;


}
}

解法二:辅助栈

  • 利用栈(或队列)遍历树的所有节点 nodenod**e ,并交换每个 node的左 / 右子节点。
1

合并两棵二叉树

题目概述:

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输入: 
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7

解法一:DFS深度优先搜索

可以使用深度优先搜索合并两个二叉树。从根节点开始同时遍历两个二叉树,并将对应的节点进行合并。

两个二叉树的对应节点可能存在以下三种情况,对于每种情况使用不同的合并方式。

如果两个二叉树的对应节点都为空,则合并后的二叉树的对应节点也为空;

如果两个二叉树的对应节点只有一个为空,则合并后的二叉树的对应节点为其中的非空节点;

如果两个二叉树的对应节点都不为空,则合并后的二叉树的对应节点的值为两个二叉树的对应节点的值之和,此时需要显性合并两个节点。

对一个节点进行合并之后,还要对该节点的左右子树分别进行合并。这是一个递归的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 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 mergeTrees(TreeNode root1, TreeNode root2) {
//对两棵树先进行判空
if(root1==null){
return root2;
}
if(root2==null){
return root1;
}

//将合并二叉树的根节点构造出来,并且开始递归,每合并完一个节点,还要递归的合并其左右子树。
TreeNode merged = new TreeNode(root1.val+root2.val);
merged.left = mergeTrees(root1.left,root2.left);
merged.right = mergeTrees(root2.right ,root1.right );
return merged;
}
}

复杂度分析:

解法二:广度优先搜索BFS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
也可以使用广度优先搜索合并两个二叉树。首先判断两个二叉树是否为空,如果两个二叉树都为空,则合并后的二叉树也为空,如果只有一个二叉树为空,则合并后的二叉树为另一个非空的二叉树。

如果两个二叉树都不为空,则首先计算合并后的根节点的值,然后从合并后的二叉树与两个原始二叉树的根节点开始广度优先搜索,从根节点开始同时遍历每个二叉树,并将对应的节点进行合并。

使用三个队列分别存储合并后的二叉树的节点以及两个原始二叉树的节点。初始时将每个二叉树的根节点分别加入相应的队列。每次从每个队列中取出一个节点,判断两个原始二叉树的节点的左右子节点是否为空。如果两个原始二叉树的当前节点中至少有一个节点的左子节点不为空,则合并后的二叉树的对应节点的左子节点也不为空。对于右子节点同理。

如果合并后的二叉树的左子节点不为空,则需要根据两个原始二叉树的左子节点计算合并后的二叉树的左子节点以及整个左子树。考虑以下两种情况:

如果两个原始二叉树的左子节点都不为空,则合并后的二叉树的左子节点的值为两个原始二叉树的左子节点的值之和,在创建合并后的二叉树的左子节点之后,将每个二叉树中的左子节点都加入相应的队列;

如果两个原始二叉树的左子节点有一个为空,即有一个原始二叉树的左子树为空,则合并后的二叉树的左子树即为另一个原始二叉树的左子树,此时也不需要对非空左子树继续遍历,因此不需要将左子节点加入队列。

对于右子节点和右子树,处理方法与左子节点和左子树相同。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* 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 mergeTrees(TreeNode root1, TreeNode root2) {
if(root1==null){
return root2;
}
if(root2==null){
return root1;
}
TreeNode merged = new TreeNode(root1.val+root2.val);
Queue<TreeNode> queue1 = new LinkedList<>();
Queue<TreeNode> queue2 = new LinkedList<>();
Queue<TreeNode> queue = new LinkedList<>();

//向队列中添加元素
queue.offer(merged);
queue1.offer(root1);
queue2.offer(root2);

while(!queue1.isEmpty()&&!queue2.isEmpty()){
TreeNode node1 = queue1.poll();
TreeNode node = queue.poll();
TreeNode node2 = queue2.poll();

TreeNode left1 = node1.left,left2 = node2.left,right1 = node1.right,right2 = node2.right;
if(left1!=null || left2!=null){
if(left1!=null&&left2!=null){
TreeNode left = new TreeNode(left1.val+left2.val);
node.left = left;
queue.offer(left);
queue1.offer(left1);
queue2.offer(left2);

}else if(left1!=null){
node.left = left1;
}else if(left2!=null){
node.left = left2;
}

}


if(right1!=null || right2!=null){
if(right1!=null&&right2!=null){
TreeNode right = new TreeNode(right1.val+right2.val);
node.right = right;
queue.offer(right);
queue1.offer(right1);
queue2.offer(right2);

}else if(right1!=null){
node.right = right1;
}else if(right2!=null){
node.right = right2;
}
}

}
return merged;

}
}

复杂度分析:

二叉搜索树的范围和

二叉树的最大深度

二叉树的层平均值

题目描述:

给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。

示例 1:

1
2
3
4
5
6
7
8
9
输入:
3
/ \
9 20
/ \
15 7
输出:[3, 14.5, 11]
解释:
第 0 层的平均值是 3 , 第1层是 14.5 , 第2层是 11 。因此返回 [3, 14.5, 11] 。

提示:

  • 节点值的范围在32位有符号整数范围内。

解法一: 深度优先搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 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<Double> averageOfLevels(TreeNode root) {
//创建两个集合,counts用来存放二叉树每一层的节点数, sums用来存放每一层的节点总和。
List<Integer> counts = new ArrayList<Integer>();
List<Double> sums = new ArrayList<Double>();
dfs(root,0,counts,sums);
List<Double> averages = new ArrayList<Double>();
int size = sums.size();
for(int i=0;i<size; i++){
averages.add(sums.get(i)/counts.get(i));
}
return averages;


}
public void dfs(TreeNode root,int level,List<Integer> counts,List<Double> sums){
if(root==null){
return ;
}
if(level<sums.size()){ //这个就是用来判断当前层节点个数是否>2,因为最初传入Level的值为0;
sums.set(level,sums.get(level)+root.val);
counts.set(level,counts.get(level)+1);

}else{
sums.add(1.0*root.val);
counts.add(1);
} //这个else的情况就是当前同层结点只有一个。sums直接添加值,而counts将当前层数节点个数设置为一个。

//开始递归
dfs(root.left,level+1,counts,sums);
dfs(root.right,level+1,counts,sums);
}
}

复杂度分析:

image-20210325152848238

解法二:广度优先搜索

复杂度分析:

剑指offer68 二叉树的最近公共祖先

题目描述

解法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private TreeNode ans;
public Solution(){
this.ans = null;
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
thist.dfs(root,p,q);
return this.ans;

}
public boolean dfs(TreeNode root,TreeNode p,TreeNode q){
if(root==null){
return false;
}

//因为从最底层子树开始递归,所以找出来的一定是最近公共子树
boolean flson = dfs(root.left,p,q);
boolean frson = dfs(root.right,p,q);
//判断是否已经找到最近公共祖先的条件
//1.左右子树均包含要找的值,满足条件,返回ans
//2.左右子树其中一个包含p或者q,另一个则是自身
if((flson&&frson) || ((root.val==p.val||root.val==q.val)&&(flson||frson))){
ans = root;
}
//递归返回值,注意给出的返回条件
return flson || frson || (root.val==p.val || root.val ==q.val);

}
}

复杂度分析:

image-20210326170817422

解法二:存储父节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
Map<Integer,TreeNode> map = new HashMap<>();
Set<Integer> set = new HashSet<>();
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
dfs(root);
while(p!=null){
//这是记录p的所有祖先的过程,这是通过节点值来标识的
set.add(p.val);
p = map.get(p.val);
}
while(q!=null){
//开始将q的祖先与所有p的祖先匹配,然后找出第一个一样的祖先。
//注意:都是从底层子树开始的
if(set.contains(q.val)){
return q;
}
//没有找到的话,继续向上找q的祖先
q = map.get(q.val);
}
return null;


}
public void dfs(TreeNode root){
if(root.left !=null){
map.put(root.left.val,root);
dfs(root.left);
}
if(root.right !=null){
map.put(root.right.val,root);
dfs(root.right);
}
}
}

复杂度分析

image-20210326174012955

谢谢你的支持哦,继续加油.