当我看着一个面试问题时出现了这个问题:
给出一组不同的整数nums
,返回所有可能的子集(幂集)。一种解决方案是:
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if(nums==null || nums.length==0) return res;
dfs(res, new ArrayList<>(), 0, nums);
return res;
}
private void dfs(List<List<Integer>> res, List<Integer> list, int pos, int[] nums) {
res.add(new ArrayList<Integer>(list));
if(pos==nums.length+1) return;
for(int i=pos; i<nums.length; i++) {
list.add(nums[i]);
dfs(res, list, i+1, nums);
list.remove(list.size()-1);
}
}
}
在辅助函数dfs
中,我们每次必须添加list
的副本。否则,空白列表将多次添加到res
中(如果执行res.add(list)
而不是解决方案中的操作)。为什么需要“新”?
答案 0 :(得分:0)
您的代码是nums
数组中数字集的所有可能子集的相当标准的深度优先递归搜索(DFS)。
当我按原样运行您的代码时,它可以很好地工作。例如,我这样称呼它:
Solution app = new Solution();
int[] nums = { 5, 2 };
List<List<Integer>> subsets = app.subsets(nums);
subsets.forEach(System.out::println);
输出:
[]
[5]
[5, 2]
[2]
您是完全正确的:如果在辅助函数dfs
中,我们没有使用new
复制列表,则结果将显示为空列表的列表。我尝试将dfs
的第一行更改为:
res.add(list);
现在与上述相同的调用的输出是:
[]
[]
[]
[]
我将告诉您什么:这不仅是4个空列表。这是相同空列表,被打印了四次。
Follow this link to see the difference live.
这是为什么?毕竟,就像您说的那样,helper方法确实将元素添加到其循环内的列表中:
list.add(nums[i]);
在没有new
的情况下,相同列表将传递给dfs
的每个递归调用。将元素添加到此列表后,它们也会再次从列表中删除:
list.remove(list.size()-1);
因此,在运行代码的大部分时间里,列表的结果列表res
将包含一个其中包含元素的列表(多次相同的列表),最后所有元素将被删除。从包含的列表中。
您知道new
所做的就是复制列表。这样,可以保留列表中包含元素的状态:将元素从原始列表中删除后,不会从副本中删除任何元素。因此,您的结果最终将包含所有不同的列表,以及所有包含列表添加到结果中的元素。
答案 1 :(得分:0)
首先,我们应该知道for循环中使用的“列表”包含指向列表类型的地址。
因此,在使用res.add(list)
时,我们将地址而不是值存储到res中。然后,每次运行list.add(nums[i])
时,我们都可以找到存储在list
中的直通地址或存储在res
中的地址(实际上它们存储相同的地址)。
随着回溯的深入,我们将相同的地址(存储在变量list
中)重复添加到res
中,这就是为什么您可以找到列表副本的原因,因为它们都是相同的地址
因此“新”对于使res存储指向不同临时列表的不同地址是必要的。
答案 2 :(得分:-1)
不,您不需要new
。
我刚刚检查了
import java.util.*;
public class Main
{
public static void addToList(List<List<Integer>> res, List<Integer> toAdd){
res.add(toAdd);
}
public static void main(String[] args)
{
List<Integer> a = new ArrayList<>();
a.add(5); a.add(2); // list a size is expected to be 2
List<Integer> b = new ArrayList<>();
b.add(3); // list b size is expected to be 1
List<List<Integer>> containerOfLists = new ArrayList<>();
addToList(containerOfLists, a);
addToList(containerOfLists, b);
// Should be 2
System.out.println("The list size is "+ containerOfLists.size());
// Should be 2
System.out.println("The nested list 0 size is "+ containerOfLists.get(0).size());
// Should be 1
System.out.println("The nested list 1 size is "+ containerOfLists.get(1).size());
// Should be 5
System.out.println("The nested list 0 element 0 is "+containerOfLists.get(0).get(0));
}
}
结果符合预期,帮助器中未使用new
并且容器不为空:
The list size is 2
The nested list 0 size is 2
The nested list 1 size is 1
The nested list 0 element 0 is 5
我不知道您在什么时候会得到一个空列表,但是,我想知道您所遇到的问题是否与您按照以下方式创建一个空列表
List<List<Integer>> res = new ArrayList<>();
在您的subsets
方法中。在那里,您可能会返回带有res对象的空列表,并且您可能会错误地认为该空列表是dfs返回的。我只能猜想。