You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中如何避免List<List<Integer>>重复列表?求更优方案

更高效的组合去重方案:回溯时直接剪枝

嘿,这个问题我之前做组合求和类题目时也碰到过!你现在用排序+HashSet的方法确实能实现去重,但不仅代码繁琐,还会因为生成大量重复组合再事后过滤而浪费时间和空间。其实我们可以在回溯搜索的过程中直接避免生成重复组合,从根源上解决问题,效率会高很多。

核心思路

  • 先对候选集合排序:排序后我们能方便地识别并跳过重复元素,同时还能提前终止不可能的递归分支(比如当前元素比剩余目标值大时,后面的元素肯定更大,直接break)。
  • 回溯时控制起始索引+跳过重复元素:在每一层递归的循环中,我们从start索引开始遍历,并且如果当前元素和前一个元素相同,且不是当前层的第一个元素,就跳过它——这样就能避免同一层中重复选择相同元素,自然不会生成顺序不同但元素相同的组合(比如[2,2,3]和[2,3,2]这种情况根本不会出现)。

具体代码实现

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CombinationSum {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        // 第一步:对候选数组排序,为剪枝做准备
        Arrays.sort(candidates);
        backtrack(candidates, target, 0, new ArrayList<>(), result);
        return result;
    }

    private void backtrack(int[] candidates, int remainingTarget, int start, List<Integer> currentCombination, List<List<Integer>> result) {
        // 找到符合条件的组合,加入结果集
        if (remainingTarget == 0) {
            result.add(new ArrayList<>(currentCombination));
            return;
        }

        for (int i = start; i < candidates.length; i++) {
            int currentNum = candidates[i];
            // 因为数组已排序,当前元素比剩余目标大,后面的元素肯定更大,直接终止循环
            if (currentNum > remainingTarget) {
                break;
            }
            // 跳过同一层的重复元素,避免生成重复组合
            if (i > start && currentNum == candidates[i-1]) {
                continue;
            }

            // 选择当前元素
            currentCombination.add(currentNum);
            // 递归搜索:这里start传i,因为允许重复选择同一个元素(比如例子中的2可以选两次)
            backtrack(candidates, remainingTarget - currentNum, i, currentCombination, result);
            // 回溯,撤销选择
            currentCombination.remove(currentCombination.size() - 1);
        }
    }

    public static void main(String[] args) {
        CombinationSum solution = new CombinationSum();
        int[] candidates = {2, 3, 6, 7};
        int target = 7;
        System.out.println(solution.combinationSum(candidates, target));
        // 输出:[[2, 2, 3], [7]]
    }
}

为什么这个方案更优?

  • 空间效率更高:不需要额外的HashSet存储中间结果,节省了内存开销。
  • 时间效率更高:回溯过程中直接剪枝,避免生成大量无效的重复组合(比如[2,3,2]这种不符合要求的组合根本不会被生成),减少了不必要的递归调用和后续处理。
  • 代码更简洁清晰:逻辑集中在回溯过程中,不需要事后对每个组合排序、去重,可读性更强。

对比你原来的方法:原来的思路是先生成所有可能的组合,再对每个组合排序后放入HashSet去重,相当于做了很多“无用功”——生成了大量不符合要求的组合,然后再丢弃。而提前剪枝的方法从根源上避免了这些无效操作。

内容的提问来源于stack exchange,提问作者Deepika Rajani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:45:58