Skip to content
本页目录

0040-组合总和II

https://leetcode.cn/problems/combination-sum-ii

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意:解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

思路

回溯法,数字不能重复使用,只能使用一次,定义 begin 变量 重复使用 used 最后集合不能重复

参考代码

csharp
public class Solution {

	private List<IList<int>> result = new List<IList<int>>();
    private bool[] used ;
    private int back;

	private List<int> CopyList(List<int> list){
		List<int> newList = new List<int>();
		for(int i=0; i<list.Count; i++){
			newList.Add(list[i]);
		}
		return newList;
	}

    private void Print(List<int> list){
        for(int i=0; i<list.Count; i++){
			Console.Write("{0} ",list[i]);
		}
        Console.WriteLine();
    }

	private void dfs(int[] candidates, int target, int begin, List<int> list){
		if(target < 0){
			return ;
		}
		if(target == 0){
			//输出结果
			result.Add(CopyList(list));
            return;
		}
		for(int i=begin; i<candidates.Length; i++){
            if(!used[i] && candidates[i] != back){
                list.Add(candidates[i]);
                used[i] = true;
                dfs(candidates, target - candidates[i], i+1,list);
                back = list[list.Count-1];
                list.RemoveAt(list.Count - 1);
                used[i] = false;
            }
		}
	}

    public IList<IList<int>> CombinationSum2(int[] candidates, int target) {
    	List<int> list = new List<int>();
        Array.Sort(candidates);
        used = new bool[candidates.Length];
    	dfs(candidates,target,0,list);
    	return result;
    }
}

参考代码2:不需要 used 数组

需要重新思考下什么时候使用 used 什么时候不用

csharp
public class Solution {
	private List<IList<int>> result = new List<IList<int>>();
    private int back;

	private List<int> CopyList(List<int> list){
		List<int> newList = new List<int>();
		for(int i=0; i<list.Count; i++){
			newList.Add(list[i]);
		}
		return newList;
	}

    private void Print(List<int> list){
        for(int i=0; i<list.Count; i++){
			Console.Write("{0} ",list[i]);
		}
        Console.WriteLine();
    }

	private void dfs(int[] candidates, int target, int begin, List<int> list){
		if(target < 0){
			return ;
		}
		if(target == 0){
			//输出结果
			result.Add(CopyList(list));
            return;
		}
		for(int i=begin; i<candidates.Length; i++){
            if( candidates[i] != back){
                list.Add(candidates[i]);
                dfs(candidates, target - candidates[i], i+1,list);
                back = list[list.Count-1];
                list.RemoveAt(list.Count - 1);
            }
		}
	}

    public IList<IList<int>> CombinationSum2(int[] candidates, int target) {
    	List<int> list = new List<int>();
        Array.Sort(candidates);
    	dfs(candidates,target,0,list);
    	return result;
    }
}

复习:20220512 去重逻辑

  1. 因为每个数字只用一次,所以先要在每次 dfs 的时候 begin
  2. 要去重,比如求和 = 8, 数据 1 2 1 5 , 每个数字只用一次会有两个结果 , 1 2 5 和 2 1 5 所以我们首先要排序变成 1,1,2,5 然后在递归的时候,第二个1不要重复计算。 我们不能使用 candidate[i] = candidate[-1] 的时候就停止处理,因为比如 1,1 后面有个 6 也需要输出。

所以去重,只能是上次参与计算过的数字, 所以我们需要从 list 中取出一个,刚刚要回溯的数字,如果他刚被回溯掉,也就是从list中删除,那我们就没有必要把它再加进去重新运算。

因为在回溯的时候,设置一个 back 的变量,在再次进入 dfs 的时候,检查是否与这个回溯的值相同,如果相同则跳过。

csharp
public class Solution {
    List<IList<int>> result = new List<IList<int>>();
    int back = -1; 
    private void dfs(int[] candidates,int begin, int target, List<int> list){
        if(target < 0){
            return;
        }
        if(target == 0){ //满足条件输出
            result.Add(new List<int>(list));
            return;
        }
        for(int i=begin; i<candidates.Length; i++){
            if(i>0 && candidates[i] == back){
                continue;       
            }
            list.Add(candidates[i]);
            dfs(candidates,i+1,target - candidates[i],list);
            back = list[list.Count-1];
            list.RemoveAt(list.Count-1); 
        }
    }

    public IList<IList<int>> CombinationSum2(int[] candidates, int target) {
        Array.Sort(candidates);
        List<int> list = new List<int>();
        dfs(candidates,0,target,list);
        return result;
    }
}

Released under the MIT License.