在List <list <integer >>`上添加一个List <integer>,为什么在这种情况下需要“ new”?

时间:2018-07-02 03:11:44

标签: java list copy

当我看着一个面试问题时出现了这个问题:

给出一组不同的整数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)而不是解决方案中的操作)。为什么需要“新”?

3 个答案:

答案 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返回的。我只能猜想。