回溯算法:List 还是 ArrayList?一个深拷贝引发的思考

news/2025/4/22 1:03:45

在学习和使用回溯算法解决问题时,我们经常会遇到需要维护一个结果列表,例如所有可能的子集、组合或排列。 这个结果列表通常是一个 List<List<Integer>>,其中内部的 List<Integer> 代表一个具体的解。

然而,在构建这些内部的 List<Integer> 时,我们应该使用 List 接口还是 ArrayList 类呢? 这个问题看似简单,但背后隐藏着一个关于深拷贝和浅拷贝的重要概念,它直接影响到回溯算法的正确性。

问题重现:回溯算法中的陷阱

让我们考虑一个简单的例子:使用回溯算法找到一个数组的所有子集。 一个常见的实现方式如下:

import java.util.ArrayList;
import java.util.List;public class Subsets {public static List<List<Integer>> subsets(int[] nums) {List<List<Integer>> res = new ArrayList<>();backtrack(nums, 0, new ArrayList<>(), res);return res;}private static void backtrack(int[] nums, int index, List<Integer> currentSubset, List<List<Integer>> res) {// 基本情况:将当前子集添加到结果列表res.add(currentSubset); // 潜在的问题!// 递归探索for (int i = index; i < nums.length; i++) {currentSubset.add(nums[i]); // 选择backtrack(nums, i + 1, currentSubset, res);currentSubset.remove(currentSubset.size() - 1); // 撤销选择 (回溯)}}public static void main(String[] args) {int[] nums = {1, 2, 3};List<List<Integer>> allSubsets = subsets(nums);System.out.println(allSubsets);}
}

展开

这段代码看起来很合理,但如果你运行它,你会发现结果是错误的! 例如,对于输入 [1, 2, 3],你可能会得到类似 [[], [], [], [], [], [], [], []] 的结果,而不是预期的 [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

问题出在哪里?

问题在于这行代码:

res.add(currentSubset);

这里,我们直接将 currentSubset 添加到 res 中,而没有进行任何拷贝。 这意味着 res 中的所有元素都指向同一个 currentSubset 对象。 当我们在回溯过程中修改 currentSubset 时,res 中的所有元素都会受到影响,最终导致错误的结果。

深拷贝的必要性

为了解决这个问题,我们需要对 currentSubset 进行深拷贝,然后再将其添加到 res 中。 深拷贝会创建一个新的 ArrayList 对象,并将 currentSubset 中的所有元素复制到这个新的 ArrayList 中。 这样,res 中的每个元素都将指向一个独立的列表,而对 currentSubset 的修改不会影响到 res 中的其他元素。

正确的代码如下:

import java.util.ArrayList;
import java.util.List;public class Subsets {public static List<List<Integer>> subsets(int[] nums) {List<List<Integer>> res = new ArrayList<>();backtrack(nums, 0, new ArrayList<>(), res);return res;}private static void backtrack(int[] nums, int index, List<Integer> currentSubset, List<List<Integer>> res) {// 基本情况:将当前子集添加到结果列表res.add(new ArrayList<>(currentSubset)); // 深拷贝!// 递归探索for (int i = index; i < nums.length; i++) {currentSubset.add(nums[i]); // 选择backtrack(nums, i + 1, currentSubset, res);currentSubset.remove(currentSubset.size() - 1); // 撤销选择 (回溯)}}public static void main(String[] args) {int[] nums = {1, 2, 3};List<List<Integer>> allSubsets = subsets(nums);System.out.println(allSubsets);}
}

展开

现在,res.add(new ArrayList<>(currentSubset)) 创建了一个 currentSubset 的副本,并将这个副本添加到 res 中。 这样,res 中的每个元素都指向一个独立的列表,结果就是正确的。


https://dhexx.cn/news/show-5497361.html

相关文章

【FPGA】——DDS信号发生器设计

目录 一 、IP核的使用 &#xff08;1)RAM IP核 (2)FIFO IP核 二、DDS信号发生器设计 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;仿真波形 一 、IP核的使用 IP核&#xff1a;ASIC或FPGA中预先设计好具有某种功能的电路模块&#xff0c;参数可修改&#xf…

【差分隐私相关概念】瑞丽差分隐私(RDP)命题2

分步解析与答案 1. c-稳定变换的定义 c-稳定变换是一种将群体数据集&#xff08;如数据库集合&#xff09;的相邻性映射到个体数据集&#xff08;如单条记录变化&#xff09;的变换。具体来说&#xff0c;若变换 g : D ′ → D g: \mathcal{D} \to \mathcal{D} g:D′→D 是 …

案例 - 登录认证:保障系统安全访问的实现

摘要&#xff1a;本文介绍了为Tlias智能学习辅助系统添加登录认证功能的过程&#xff0c;涵盖从需求分析、接口文档设计&#xff0c;到思路分析、功能开发以及最后的测试等多个关键环节&#xff0c;旨在实现只有通过登录认证的用户才能安全访问后台系统功能的目标。 关键词&am…

智慧城市:如同为城市装上智能大脑,开启智慧生活

智慧城市的概念随着信息技术的飞速发展而逐渐兴起&#xff0c;它通过集成物联网、大数据、人工智能和数字孪生等先进技术&#xff0c;为城市管理和居民生活带来了前所未有的智能化变革。本文将深入探讨这些核心技术及其在智慧城市的典型应用场景&#xff0c;展示智慧城市如何提…

如何解决服务器文件丢失或损坏的问题?

# 服务器文件丢失或损坏问题的全面解决方案 ## 一、立即响应措施 ### 1. 停止写入操作 bash # 立即停止相关服务防止进一步覆盖 systemctl stop affected-service # 必要时将文件系统挂载为只读 mount -o remount,ro /affected_directory ### 2. 评估损坏范围 bash # 检查…

关于我的服务器

最近我买了台腾讯云服务器&#xff0c;然后新手小白只会用宝塔。。。 安装完之后默认的端口是8888&#xff0c;打开面板就会提示我有风险。然后 我改了端口之后&#xff0c;怎么都打不开。 于是 学到了几句命令可以使用&#xff1a; //查看端口是否已经修改成功 cat www/se…

flutter doctor 信号号超时

报错如下&#xff1a; :\Users\Administrator>flutter doctor Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel stable, 3.27.4, on Microsoft Windows [版本 10.0.22631.5189], locale zh-CN) [√] Windows Version (Installed versi…

网络的起点:深入解析计算机网络中的网络接口层

一、什么是网络接口层&#xff1f; 计算机网络的 网络接口层&#xff08;Network Interface Layer&#xff09;&#xff0c;在 TCP/IP模型 中处于最底层&#xff0c;负责将数据从计算机传输到物理网络媒介&#xff0c;并在此基础上确保数据的正确传输。它位于数据链路层和物理…

在IDEA里面建立maven项目(便于java web使用)

具体步骤&#xff1a; 第一次有的电脑你再创建项目的时候右下角会提醒你弹窗&#xff1a;让你下载没有的东西 一定要下载&#xff01;&#xff01;可能会很慢 运行结果&#xff1a; 因为他是默认的8080端口所以在运行的时候输入的url如下图&#xff1a; 新建了一个controller代…

ERR_PNPM_DLX_NO_BIN No binaries found in tailwindcss

场景复现&#xff1a; 最近在vue3项目中安装了tailwindcss&#xff0c;但是它默认帮我安装的版本是4XX的&#xff0c;导致我执行 npx tailwindcss init -p报错了。 解决方案&#xff1a; 更改tailwindcss的版本为3 pnpm add -D tailwindcss3再次执行生成tailwindcss的初始…