39. 组合总和
题目
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例 1:
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
示例 2:
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2], target = 1
输出: []
提示:
1 <= candidates.length <= 30
2 <= candidates[i] <= 40
candidates 的所有元素 互不相同
1 <= target <= 40
解题思路
- 从候选数组的第一个数字开始尝试
- 对于每个数字,我们可以选择用它或不用它
- 如果选择用它,我们可以继续选择它(因为可以重复使用)
- 如果当前和超过目标值,我们就回溯
- 如果当前和等于目标值,我们就找到了一个有效组合
代码实现
function combinationSum(candidates: number[], target: number): number[][] {
const result: number[][] = [];
function backtrack(start: number, combination: number[], sum: number): void {
if (sum === target) {
result.push([...combination]);
return;
}
if (sum > target) {
return;
}
for (let i = start; i < candidates.length; i++) {
combination.push(candidates[i]);
backtrack(i, combination, sum + candidates[i]);
combination.pop();
}
}
backtrack(0, [], 0);
return result;
}
40. 组合总和 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
解题思路
- 该题目回溯时要考虑重复元素,这是关键点
- 首先对数组进行排序,这样相同的元素会相邻
- 在每一层的循环中,跳过重复的元素,以避免生成重复的组合
- 在递归时,将索引加1,因为每个元素只能使用一次
代码实现
function combinationSum2(candidates: number[], target: number): number[][] {
const result: number[][] = [];
candidates.sort((a, b) => a - b);
function backtrack(start: number, tempArr: number[], sum: number): void {
if (sum === target) {
result.push([...tempArr]);
return;
}
if (sum > target) {
return;
}
for (let i = start; i < candidates.length; i++) {
// 跳过重复元素
if (i > start && candidates[i] === candidates[i - 1]) continue;
tempArr.push(candidates[i]);
backtrack(i + 1, tempArr, sum + candidates[i]);
tempArr.pop();
}
}
backtrack(0, [], 0);
return result;
}
131. 分割回文串
题目
给你一个字符串 s,请你将 **s **分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
示例 1:
输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]
示例 2:
输入:s = "a"
输出:[["a"]]
提示:
1 <= s.length <= 16
s 仅由小写英文字母组成
解题思路
- 使用回溯算法解题,收集分割结果为回文串的状态
- 当起始点等同于字符串长度时终止递归,每次分割都应该确保加入已分割的部分为回文串,这才有继续分割的必要性
代码实现
function partition(s: string): string[][] {
const result: string[][] = [];
function isPalindrome(start: number, end: number): boolean {
while (start < end) {
if (s[start] !== s[end]) return false;
start++;
end--;
}
return true;
}
function backtrack(start: number, tempArr: string[]) {
if (start === s.length) {
result.push([...tempArr]);
return
}
for (let i = start; i < s.length; i++) {
if (isPalindrome(start, i)) {
tempArr.push(s.substring(start, i + 1));
backtrack(i + 1, tempArr);
tempArr.pop()
}
}
}
backtrack(0, [])
return result
};