组合总和 II(Java)

it2025-08-15  16

给定一个数组 arr 和一个目标数 target ,找出 arr 中所有可以使数字和为 target 的组合。

arr 中的每个数字在每个组合中只能使用一次。 说明:     所有数字(包括目标数)都是正整数。     解集不能包含重复的组合。

示例 1: 输入: arr = [10,1,2,7,6,1,5], target = 8, 所求解集为: [   [1, 7],   [1, 2, 5],   [2, 6],   [1, 1, 6] ]

示例 2: 输入: arr = [2,5,2,1,2], target = 5, 所求解集为: [   [1,2,2],   [5] ]

需要求出所有和为 target 的组合,并且每个数只能使用一次,因此可以使用递归 + 回溯的方法:     用 combinationSumIIDFS(pos,rest) 表示递归的函数,其中 pos 表示当前递归到了数组 arr 中的第 pos 个数,而 rest 表示还需要选择和为 rest 的数放入列表作为一个组合;     对于当前的第 pos 个数,有两种方法:选或者不选。如果选了这个数,那么调用 combinationSumIIDFS(pos+1,rest−arr[pos]) 进行递归,注意这里必须满足 rest≥arr[pos]。如果不选这个数,那么调用 combinationSumIIDFS(pos+1,rest) 进行递归;     在某次递归开始前,如果 rest 的值为 0,说明找到了一个和为 target 的组合,将其放入答案中。每次调用递归函数前,如果选了那个数,就需要将其放入列表的末尾,该列表中存储了选的所有数。在回溯时,如果选了那个数,就要将其从列表的末尾删除。 上述算法就是一个标准的递归 + 回溯算法,但是它并不适用于本问题求解。这是因为题目描述中规定了解集不能包含重复的组合,而上述的算法中并没有去除重复的组合。

因此,需要改进上述算法,在求出组合的过程中就进行去重的操作。考虑将相同的数放在一起进行处理,如果数 x 出现了 y 次,那么在递归时一次性地处理它们,即分别调用选择 0,1,⋯ ,y 次 x 的递归函数。这样就不会得到重复的组合。     使用一个哈希映射(HashMap)统计数组 arr 中每个数出现的次数。在统计完成之后,将结果放入一个列表 nums 中,方便后续的递归使用。列表 nums 的长度即为数组 arr 中不同数的个数。其中的每一项对应着哈希映射中的一个键值对,即某个数以及它出现的次数。     在递归时,对于当前的第 pos 个数,它的值为 nums[pos][0],出现的次数为 nums[pos][1],那么可以调用 combinationSumIIDFS(pos+1,rest−i×nums[pos][0])     即选择了这个数 i 次。这里 i 不能大于这个数出现的次数,并且 i×nums[pos][0] 也不能大于 rest。同时,我们需要将 i 个 nums[pos][0] 放入列表中。 这样一来,就可以不重复地枚举所有的组合了。

package com.loo;

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

public class CombinationSumII {

    public static List<int[]> nums = new ArrayList<int[]>();     public static List<List<Integer>> list = new ArrayList<List<Integer>>();     public static List<Integer> coms = new ArrayList<Integer>();          public static void main(String[] args) {         int[] arr1 = {2,3,6,7};         int target = 7;         List<List<Integer>> lists = getCombinationSumII(arr1 , target);         for (List<Integer> l : lists) {             System.out.println(l);         }         int[] arr2 = {2,3,5};         target = 8;         nums.clear();         list.clear();         coms.clear();         lists = getCombinationSumII(arr2 , target);         for (List<Integer> l : lists) {             System.out.println(l);         }         int[] arr3 = {10,1,2,7,6,1,5};         target = 8;         nums.clear();         list.clear();         coms.clear();         lists = getCombinationSumII(arr3 , target);         for (List<Integer> l : lists) {             System.out.println(l);         }         int[] arr4 = {2,5,2,1,2};         target = 5;         nums.clear();         list.clear();         coms.clear();         lists = getCombinationSumII(arr4 , target);         for (List<Integer> l : lists) {             System.out.println(l);         }     }          public static List<List<Integer>> getCombinationSumII(int[] arr , int target) {         if (arr == null || arr.length == 0) {             return list;         }         Arrays.sort(arr);         for (int n : arr) {             int len = nums.size();             if (nums.isEmpty() || n!=nums.get(len-1)[0]) {                 nums.add(new int[]{n , 1});             } else {                 ++nums.get(len-1)[1];             }         }         combinationSumIIDFS(0 , target);         return list;     }          public static void combinationSumIIDFS(int pos , int target) {         if (target == 0) {             list.add(new ArrayList<Integer>(coms));             return;         }         if (pos == nums.size() || nums.get(pos)[0] > target) {             return;         }         combinationSumIIDFS(pos + 1 , target);         int times = Math.min(target/nums.get(pos)[0], nums.get(pos)[1]);         for (int i=1;i<=times;i++) {             coms.add(nums.get(pos)[0]);             combinationSumIIDFS(pos+1 , target-i*nums.get(pos)[0]);         }         for (int i=1;i<=times;i++) {             coms.remove(coms.size()-1);         }     }

}

 

 

 

最新回复(0)