算法训练 Day 27 | 回溯算法:39. 组合总和,40.组合总和II,131.分割回文串

39. 组合总和

39. 组合总和

  •  第一想法:回溯+剪枝
  • 看到题解想法:
    1. 遍历的横竖集合应该是什么
    2. 思考如何剪枝——本题可以用排序来剪枝
  • 遇到困难:
 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

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.分割回文串

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值