题目描述
给定一个整数数组 nums 和一个目标分数 target,你需要从数组中选择一些元素(可以重复选择),使得这些元素的总和恰好等于目标分数 target。返回所有可能的组合(每个组合中的元素按非递减顺序排列),并且每个元素只能使用一次(即不允许重复选择同一个元素)。
输入:
- nums:一个整数数组,其中 1 <= nums.length <= 20,-100 <= nums[i] <= 100。
- target:一个整数,-1000 <= target <= 1000。
输出:
- 所有满足条件的组合,每个组合中的元素按非递减顺序排列。
注意:
- 结果中的每个组合中的元素不能重复。
- 返回的结果需要按组合中元素的非递减顺序排列,且结果中的组合也需要按非递减顺序排列。
思路
这个问题可以通过回溯法来解决。回溯法是一种通过递归和剪枝来搜索所有可能解的方法。具体步骤如下:
- 排序:首先将数组进行排序,这样可以方便后续的组合生成和剪枝。
- 回溯函数:定义一个回溯函数,该函数接收当前组合、当前索引和目标值作为参数。
- 递归和剪枝:
- 在每一步,可以选择当前索引的元素加入组合,并更新目标值。
- 如果目标值变为0,表示找到了一个满足条件的组合,将其加入结果集。
- 如果目标值小于0,或者当前索引已经遍历完,则回溯。
- 否则,继续递归调用回溯函数,但索引需要加1,以避免使用重复元素。
- 结果输出:最终返回所有满足条件的组合。
Java 代码解析
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CombinationSum {
public List<List<Integer>> combinationSum2(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
backtrack(result, new ArrayList<>(), nums, target, 0);
return result;
}
private void backtrack(List<List<Integer>> result, List<Integer> tempList, int[] nums, int remain, int start) {
if (remain == 0) {
result.add(new ArrayList<>(tempList));
return;
}
if (remain < 0 || start >= nums.length) {
return;
}
for (int i = start; i < nums.length; i++) {
// 避免使用重复元素
if (i > start && nums[i] == nums[i - 1]) {
continue;
}
tempList.add(nums[i]);
backtrack(result, tempList, nums, remain - nums[i], i + 1);
tempList.remove(tempList.size() - 1);
}
}
public static void main(String[] args) {
CombinationSum cs = new CombinationSum();
int[] nums = {10, 1, 2, 7, 6, 1, 5};
int target = 8;
System.out.println(cs.combinationSum2(nums, target));
}
}
C++ 代码解析
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
backtrack(result, vector<int>(), nums, target, 0);
return result;
}
private:
void backtrack(vector<vector<int>>& result, vector<int>& tempList, const vector<int>& nums, int remain, int start) {
if (remain == 0) {
result.push_back(tempList);
return;
}
if (remain < 0 || start >= nums.size()) {
return;
}
for (int i = start; i < nums.size(); ++i) {
// 避免使用重复元素
if (i > start && nums[i] == nums[i - 1]) {
continue;
}
tempList.push_back(nums[i]);
backtrack(result, tempList, nums, remain - nums[i], i + 1);
tempList.pop_back();
}
}
};
int main() {
Solution solution;
vector<int> nums = {10, 1, 2, 7, 6, 1, 5};
int target = 8;
vector<vector<int>> result = solution.combinationSum2(nums, target);
for (const auto& combination : result) {
for (int num : combination) {
cout << num << " ";
}
cout << endl;
}
return 0;
}
Python 代码解析
def combinationSum2(nums, target):
def backtrack(result, tempList, remain, start):
if remain == 0:
result.append(tempList[:])
return
if remain < 0 or start >= len(nums):
return
for i in range(start, len(nums)):
# 避免使用重复元素
if i > start and nums[i] == nums[i - 1]:
continue
tempList.append(nums[i])
backtrack(result, tempList, remain - nums[i], i + 1)
tempList.pop()
result = []
nums.sort()
backtrack(result, [], target, 0)
return result
# 测试
nums = [10, 1, 2, 7, 6, 1, 5]
target = 8
print(combinationSum2(nums, target))