为什么输出的num一直不正确?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
private int num;
private boolean[] vis;

public boolean canVisitAllRooms(List<List<Integer>> rooms) {
int n = rooms.size(), num = 0;
vis = new boolean[n];
dfs(rooms, 0);
return num == n;
}

private void dfs(List<List<Integer>> rooms, int i) {
vis[i] = true;
System.out.println(i);
num ++;
System.out.println("num为:" + num);
for (int x : rooms.get(i)) {
if (!vis[x]) {
dfs(rooms, x);
}
}
}
}

答案:

因为函数中的局部变量num会出现遮挡全局变量num的问题,导致你以为dfs中的num已经初始化,结果并没有。

解决方法:

1
2
int n = rooms.size();
num = 0; // 直接对全部变量赋值

为什么第一段代码需要额外赋值?

1
2
3
4
5
6
List<Integer>[] adj = new ArrayList[n]; // 1
Arrays.setAll(adj, _ -> new ArrayList<>());
adj[0].add(1);
adj[0].add(2);

List<List<Integer>> res = new ArrayList<>(); // 2

答案:

第一段代码中,当执行List<Integer>[] adj = new ArrayList[n]之后,数组中的每个值被初始化为 null ,如果不对每个值进行 new 创建空对象,后面 adj[0].add() 会报错。

而第二段代码,每个值都已经创建了空对象。它等价于这种写法:List<List<Integer>> res = new ArrayList<List<Integer>>();

知识点:

Java语言中,对于一个数组:

如果数组元素类型是引用类型,数组值则默认为null。因此对于List<Integer>[]来说,数组在每个元素默认为 null 。

如果是基本类型,每个元素的默认值为其数据类型对应的默认值。

以下是对于不同类型的数组,是否需要再额外的初始化的示例:(这里表述的不是很恰当)

数据类型 数组创建后默认值 通常是否需要额外初始化 说明
String[] null 视情况而定 如果需要非null值,就必须初始化
List<>[] null 必须初始化 几乎总是需要可用的集合实例
Object[] null 必须初始化 需要具体的对象实例
int[] 0 通常不需要 0本身就是有意义的数值
boolean[] false 通常不需要 false本身就是有意义的布尔值

为什么下面代码中 res 一直返回空?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
path.add(0);
dfs(graph, 0);
return res;
}

private void dfs(int[][] graph, int i) {
if (i == graph.length - 1) {
res.add(path);
return ;
}
for (int e : graph[i]) {
path.add(e);
dfs(graph, e);
path.remove(path.size() - 1);
}
}
}

答案:

因为list.add添加的是引用,而path在回溯过程中会一直变化,最后会变为空。

解决方法:

新建一个对象即可,传入新建对象的引用。

1
res.add(new ArrayList<>(path)); 

为什么 toRemove 变量需要初始化?

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
import java.util.*;

class TaskManager {

private PriorityQueue<List<Integer>> tasks;

public TaskManager(List<List<Integer>> tasks) {
this.tasks = new PriorityQueue<>(
(a, b) -> a.get(2).equals(b.get(2)) ? b.get(1) - a.get(1) : b.get(2) - a.get(2)
);
for (List<Integer> task : tasks) {
this.tasks.add(new ArrayList<>(task));
}
}

public void add(int userId, int taskId, int priority) {
tasks.add(new ArrayList<>(Arrays.asList(userId, taskId, priority)));
}

public void edit(int taskId, int newPriority) {
List<Integer> toRemove;
for (List<Integer> task : tasks) {
if (task.get(1) == taskId) {
toRemove = task;
break;
}
}
if (toRemove != null) {
tasks.remove(toRemove);
tasks.add(new ArrayList<>(Arrays.asList(toRemove.get(0), taskId, newPriority)));
}
}

public void rmv(int taskId) {
List<Integer> toRemove;
for (List<Integer> task : tasks) {
if (task.get(1) == taskId) {
toRemove = task;
break;
}
}
if (toRemove != null) {
tasks.remove(toRemove);
}
}

public int execTop() {
if (tasks.isEmpty()) {
return -1;
}
return tasks.poll().get(0);
}
}

答:因为 List<Integer> toRemove;是局部变量,局部变量系统不会自动初始化。且虽然在 for 循环中执行了赋值语句,但是编译器不确定for 循环以及 if 语句是否被执行,因此编译不通过。应提前初始化为 null,否则编译器会报错variable a might not have been initialized。

知识点:Java 变量的默认值以及初始化情况

以下为不同数据类型的默认值:

数据类型 默认值
byte 0
short 0
int 0
long 0L
char ‘u0000’
float 0.0f
double 0
boolean false
所有引用类型 null

但并不是所有未被初始化的变量都会有默认值。

例如以下代码:

1
2
3
4
5
6
public static void main(String[] args) {
int[] arr = new int[10];
System.out.println(arr[0]);
}

//打印: 0
1
2
3
4
5
6
public static void main(String[] args) {
int a;
System.out.println(a);
}

//编译失败,提示“Error:(16, 28) java: 可能尚未初始化变量a”

下面是不同类型的变量的初始化情况:

变量类型 是否会初始化
成员变量(实例变量) 系统会初始化默认值
类变量(静态变量) 系统会初始化默认值
局部变量(本地变量) 系统不会初始化默认值
参数 无需初始化默认值

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Cat {
// 实例变量,也就是成员变量。在创建对象时,成员变量在内存分配阶段被赋予默认值。在执行new指令后,被赋予程序自定义的初始值
int age;
}

public class Cat {
// 类变量。在类加载的准备阶段,被赋予默认值。在类加载的初始化阶段,被程序赋予自定义的初始值
static int age;
}

public static void main(String[] args) {
// 本地变量,必须显示赋值,否则会编译错误
int a = 0;
int[] arr;
System.out.println(arr); // java: 可能尚未初始化变量arr
}

// args 就是参数,在调用该方法是传入值
public static void main(String[] args) {

}