39. 组合总和
- 第一想法:回溯+剪枝
- 看到题解想法:
- 遍历的横竖集合应该是什么
- 思考如何剪枝——本题可以用排序来剪枝
- 遇到困难:
func combinationSum(candidates []int, target int) [][]int {
var backTrack func(arr []int, target int, startIndex int)
res := make([][]int, 0)
path := make([]int, 0)
pathSum := 0
// 排序,为了后面剪枝做准备
sort.Ints(candidates)
backTrack = func(arr []int, target int, startIndex int) {
if pathSum == target {
tmp := make([]int, len(path))
copy(tmp, path)
res = append(res, tmp)
return
}
if pathSum > target {
return
}
//剪枝:后面和大于target就不用遍历了
for i := startIndex; i < len(arr) && arr[i] <= (target - pathSum); i++ {
path = append(path, arr[i])
pathSum += arr[i]
backTrack(arr, target, i)
path = path[:len(path) - 1]
pathSum -= arr[i]
}
}
backTrack(candidates, target, 0)
return res
}
- 总结与收获:用时20min
40.组合总和II
- 第一想法:回溯,但是思考了一阵子如何保证回溯深度不被剪枝
- 看到题解想法:循环开始让i > startIndex的话,纵向元素重复也可以取到值
Java
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
int sum = 0;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backTrack(candidates, target, 0);
return result;
}
public void backTrack(int[] candidates, int target, int startIndex) {
if (target <= sum) {
if (target == sum) {
result.add(new ArrayList<>(path));
}
return;
}
for (int i = startIndex; i < candidates.length && sum + candidates[i] <= target; i++) {
// 剪枝:保留第一个元素 ——> 横剪,纵不剪;i > startIndex保证了纵不被剪掉
if (i > startIndex && candidates[i] == candidates[i - 1]) continue;
path.add(candidates[i]);
sum += candidates[i];
backTrack(candidates, target, i + 1);
path.removeLast();
sum -= candidates[i];
}
}
}
Go
func combinationSum2(candidates []int, target int) [][]int {
var backTrack func(arr []int, target int, startIndex int)
res := make([][]int, 0)
path := make([]int, 0)
sum := 0
sort.Ints(candidates)
backTrack = func(arr []int, target int, startIndex int) {
if target == sum {
tmp := make([]int, len(path))
copy(tmp, path)
res = append(res, tmp)
return
}
if target < sum {
return
}
for i := startIndex; i < len(arr) && arr[i] <= (target - sum); i++ {
// 剪枝:保留第一个元素 ——> 横剪,纵不剪;i > startIndex保证了纵不被剪掉
if i > startIndex && arr[i] == arr[i - 1] {
continue
}
path = append(path, arr[i]);
sum += arr[i]
backTrack(arr, target, i + 1)
path = path[:len(path) - 1]
sum -= arr[i]
}
}
backTrack(candidates, target, 0)
return res
}
- 总结与收获:二刷,循环开始的条件没有一下子想出来。用时:20min
131.分割回文串
- 第一想法:不知道怎么用回溯法解决,分割子串的位置不好确定
- 看到题解想法:从当前字符右侧进行分割:[startIndex, i + 1]
- 遇到困难:判断回文子串的函数可以使用动态规划进行优化,待理解
Go
func partition(s string) [][]string {
res := make([][]string, 0)
path := make([]string, 0)
var backTrack func(s string, startIndex int)
backTrack = func(s string, startIndex int) {
if startIndex >= len(s) {
tmp := make([]string, len(path))
copy(tmp, path)
res = append(res, tmp)
return
}
// 从当前索引字符右侧进行分割
for i := startIndex; i < len(s); i++ {
str := s[startIndex:i + 1]
if isPalindrome(str) == true {
path = append(path, str)
backTrack(s, i + 1)
path = path[:len(path) - 1]
} else {
continue
}
}
}
backTrack(s, 0)
return res
}
func isPalindrome(s string) bool {
if len(s) == 0 {
return false
}
left := 0
right := len(s) - 1
for left < right {
if s[left] != s[right] {
return false
}
left++
right--
}
return true
}
- 总结与收获:学习了如何用回溯法进行字符串分割,用时30min