Java—为什么必须为每个回溯基本情况创建一个新实例?

Java—为什么必须为每个回溯基本情况创建一个新实例?,java,object,permutation,backtracking,Java,Object,Permutation,Backtracking,回溯的一个经典示例是生成整数列表的所有(不一定是不同的)排列 举个例子,我写了一个函数,它可以: List<List<Integer>> perm_set = new ArrayList<>(); // store permutations private void permute(int[] nums, List<Integer> perm, boolean[] used) { if (perm.size() == nums.lengt

回溯的一个经典示例是生成整数列表的所有(不一定是不同的)排列

举个例子,我写了一个函数,它可以:

List<List<Integer>> perm_set = new ArrayList<>(); // store permutations

private void permute(int[] nums, List<Integer> perm, boolean[] used) {
    if (perm.size() == nums.length) {
        // System.out.println(perm);
        perm_set.add(new ArrayList<>(perm));  // why is it necessary to add new?
        return;
    }

    for (int i = 0; i < nums.length; i++) {
        if (used[i]) {
            continue;
        }
        used[i] = true;
        perm.add(nums[i]);
        permute_simple(nums, perm, used);
        used[i] = false;
        perm.remove(perm.size() - 1);
    }
}
List perm_set=new ArrayList();//存储置换
私有void置换(使用int[]nums、List置换、boolean[]{
if(perm.size()=nums.length){
//系统输出打印项次(perm);
perm_set.add(new ArrayList(perm));//为什么需要添加新的?
返回;
}
对于(int i=0;i
上面的函数可以工作,但我不明白为什么每次添加排列时,即每次回溯搜索完成时,都需要
新建ArrayList(perm)
。我添加了一个print语句,试图看看发生了什么,果然排列打印得很好。但是如果我只是使用
perm\u set.add(perm)
,我会得到一个空白列表列表

那么,如果在每次搜索结束时当前排列的状态是正确的,为什么我需要为每个案例创建一个新的副本呢

请注意,该示例是任意的。这个问题不特定于我提供的代码,也不特定于排列。它更广泛地适用于典型的DFS问题。我想了解为什么通常不能直接存储最终结果,以及为什么必须使用
new
关键字


谢谢

请考虑以下几点:

这将完全符合预期

@Test
public void testPass() {

    ArrayList<String> listA = new ArrayList<String>();
    listA.add("a");

    ArrayList<String> listB = new ArrayList<String>(listA);

    Assert.assertTrue(listB.get(0).equals("a"));
}
@测试
公共void testPass(){
ArrayList listA=新的ArrayList();
列表a.添加(“a”);
ArrayList listB=新的ArrayList(listA);
Assert.assertTrue(listB.get(0.equals)(“a”);
}
这将失败,因为如果在复制后修改原始列表

@Test
public void testFail() {

    ArrayList<String> listA = new ArrayList<String>();

    ArrayList<String> listB = new ArrayList<String>(listA);

    // A, modified after the copy has been made
    listA.add("a");

    Assert.assertTrue(listB.get(0).equals("a"));
    // This will throw and IndexOutOfBoundsException, as listB is empty.
}
@测试
公共void testFail(){
ArrayList listA=新的ArrayList();
ArrayList listB=新的ArrayList(listA);
//A,复制完成后修改
列表a.添加(“a”);
Assert.assertTrue(listB.get(0.equals)(“a”);
//这将抛出和IndexOutOfBoundsException,因为listB为空。
}
这将失败,因为
listB
为空。在修改前,已向其提供了一份listA的副本。

(这是一次尝试,旨在获得更一般性的答案,认识到该问题的目的是关于DFS的一般性问题。)

那么,如果每次搜索结束时的当前排列状态是正确的,为什么我必须为每个案例创建一个新的
perm

简短回答:这是必要的,因为您要更改
perm


更长的回答:你的
列表什么是
perm\u set
?这是唯一一次在发布的代码中使用它。还有,什么是
nums
?这个问题缺少很多细节和相关的背景。是的,它缺少细节,因为例子并不重要。该示例仅用于演示有关回溯的一般概念问题。你所问的关于变化的变量都没有,也没有一个有助于回答基本问题。我在问为什么每次搜索结束时都需要使用
new
关键字。置换示例是任意的,可能是任何其他典型的DFS问题。现在由于你不必要的否决票,我不太可能得到有助于我了解这种行为的回复。因为如果你只是添加
perm
,它仍然可以在外部修改。通过创建一个新列表,您可以确保一旦添加到
perm\u集
数组中,对
perm
变量的修改不会影响已添加到
perm\u集
的列表,因此对
perm
的进一步修改仍然会影响列表中已有的一些
perm
?这是因为列表是可变的吗?这就是为什么在处理字符串等问题时不需要一个新实例的原因。我不相信所示的代码真的能工作。首先,缺少
perm\u simple
的定义。啊,明白了。因此,这就像不创建新的实例一样,它们都指向同一个对象,因此所有修改都将应用于每个指针的引用。正确的是,使用旧数组创建一个新数组基本上会创建数组的副本,以便以后的修改不会影响原始集合。它不仅仅是
新建
,而是将原点传递给。