result.add(list)和result.add(new ArrayList<Integer>(list))的深浅拷贝问题

本文通过一道二叉树算法题,解析了深拷贝与浅拷贝的区别,并通过实例展示了两种拷贝方式下集合变化的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题概述

最近在做Leetcode中一道二叉树算法题 [二叉树中和为某一值的路径] 时遇到了一个小问题,result是符合条件的结果集,list是所有路径和的待选结果集,当判定list中存储的待选结果符合条件,准备存入result时我直接使用的result.add(list);而没有考虑深浅拷贝的问题,导致后续回溯剪枝操作造成result集错误。

深浅拷贝语法

result.add(list); 属于浅拷贝,当添加一个list后,如果list改变,result值也会随之发生变化,尤其注意之前已经存进去的lsit会变;
result.add(new ArrayList<Integer>(list));属于深拷贝,当添加一个list后,如果list改变,result之前存储的值不会发生变化;
所以这道算法题如果直接使用浅拷贝添加list,那么当回溯到上一个节点,也就是删除掉list的最后一个节点时,已经存储在待选结果中的 list 中的某条路径和也会发生改变。

代码测试

public static void main(String[] args) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println(list); //[1, 2, 3]

        /**
         * 浅拷贝中,当添加一个list后,如果list改变,result值也会随之发生变化,尤其注意之前已经存进去的lsit会变;
         */
        result.add(list);  //浅拷贝
        System.out.println(result); //[[1, 2, 3]]
        list.add(4);
        result.add(list);
        System.out.println(result); //[[1, 2, 3, 4], [1, 2, 3, 4]]

        //清空列表
        list.clear();
        result.clear();
        System.out.println(list);
        System.out.println(result);

        /**
         * 深拷贝中,当添加一个list后,如果list改变,result之前存储的值不会发生变化;
         */
        result.add(new ArrayList<Integer>(list));  //深拷贝
        System.out.println(result); //[[]]
        list.add(4);
        result.add(list);
        System.out.println(result); //[[], [4]]
    }
在这段代码中,`subsets`函数用于生成给定数组的所有子集。但是存在一些问题导致结果不符合预期: ### 错误分析 1. **路径添加到结果列表的方式不对**: - `res.add(path);` 这里直接将引用加入到了结果集中,在后续操作修改了 `path` 后会影响到已经存入的结果。 2. **递归终止条件不合理**: - 当前判断是否到达叶子节点只考虑了长度等于输入数组的情况,但实际上应该允许所有可能的组合都被记录下来。 3. **初始状态下的空集合未处理好**: - 初始状态下应先插入一个空集作为第一个元素。 下面是修正后的版本,并附带解释: ```java class Solution { private List<List<Integer>> result = new ArrayList<>(); // 存储最终返回的答案 public List<List<Integer>> subsets(int[] nums) { backtrack(nums, 0, new ArrayList<>()); return result; } private void backtrack(int[] nums, int startIdx, List<Integer> currentPath) { // 每次进入这个函数就保存当前的状态(深拷贝) result.add(new ArrayList<>(currentPath)); // 如果起始索引超出范围,则结束回溯过程 if (startIdx >= nums.length) return; // 遍历从当前位置开始的所有剩余选项 for (int idx = startIdx; idx < nums.length; ++idx) { // 做选择:把数字放进临时容器内 currentPath.add(nums[idx]); // 继续探索下一个决策点 backtrack(nums, idx + 1, currentPath); // 取消上一步的选择以便尝试其他可能性 currentPath.remove(currentPath.size() - 1); } } } ``` #### 主要改进点说明: - 使用了新的局部变量`currentPath` 来存储每条分支的具体内容; - 在每次调用`result.add()`之前都创建了一个全新的ArrayList实例来保存当时的值副本; - 修改了递归出口条件以保证能够遍历完整个树形结构; 通过以上调整可以正确地获得所有的非重复子集。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值