代码随想录算法训练营第二十六天|39. 组合总和、40. 组合总和 II、131. 分割回文串

LeetCode 39. 组合总和
题目链接:39. 组合总和 - 力扣(LeetCode)

​思路:

同一个数字可以无限制的重复被选取,因此可以不断地进行选取,但是要注意要求组合不同,因此写个判断函数可以判断一下新得到的组合是否已经出现在res中了。

在第一段代码中,dfs(i)的i完全没派上用场,没有限制candidates的下标,因此有大量重复元素出现。比如[2,2,4]与[2,4,2]虽然会被查重函数排除掉,但是在生成path时做了很多无用功,最后导致了代码运行超时。
在第二段代码中,每次添加删除的就不是for循环遍历的candidates元素,而是通过下标元素来固定之前选取过的元素不会再被使用。

代码:

#python 超出时间限制
class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res, path = [], []
        if not candidates:
            return res
        candidates.sort(reverse = False)
        def check_is_same(a, b):
            for x in b:
                if Counter(a) == Counter(x):
                    return True
            return False
        def dfs(i):
            if sum(path) == target and not check_is_same(path.copy(), res):
                res.append(path.copy())
            diff = target - sum(path)
            for j in candidates:
                if j <= diff:
                    path.append(j)
                    dfs(i + 1)   //这样写,i没起到作用,其实可以把候选数组排序这样来确定下标,一旦遍历过的下标就过去不要再考虑了,我这样写重复考虑了所有元素,虽然写了一个check_is_same函数来去重但是仍然会超时。
                    path.pop()
                else:
                    break
        dfs(0)
        return res

#python 正常运行,未超时
class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res, path = [], []
        if not candidates:
            return res
        candidates.sort()
        def check_is_same(a, b):
            for x in b:
                if Counter(a) == Counter(x):
                    return True
            return False
        def dfs(i):
            if sum(path) == target and not check_is_same(path, res):
                res.append(path.copy())
            elif sum(path) < target:
                for j in range(i, len(candidates)):   //在i的后面选取candidates中的元素,避免之前元素的再次出现。
                    if candidates[j] <= target - sum(path):
                        path.append(candidates[j])
                        dfs(j)
                        path.pop()
        dfs(0)
        return res
/java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates); // 对数组进行排序
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        traversal(candidates, target, 0, path, res);
        return res;
    }

    private void traversal(int[] candidates, int target, int start, List<Integer> path, List<List<Integer>> res) {
        if (target == 0) {
            // 找到一个有效的组合,添加到结果列表中
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = start; i < candidates.length; i++) {
            // 跳过重复的元素
            if (i > start && candidates[i] == candidates[i - 1]) {
                continue;
            }

            // 如果当前元素大于剩余目标,直接返回
            if (candidates[i] > target) {
                break;
            }

            // 选择当前元素
            path.add(candidates[i]);
            // 递归地寻找剩余目标的组合
            traversal(candidates, target - candidates[i], i, path, res);
            // 回溯,移除当前元素
            path.remove(path.size() - 1);
        }
    }
}

LeetCode 40. 组合总和 II
题目链接:40. 组合总和 II - 力扣(LeetCode)

​思路:

与上一题不同的是:同一个数字不能被重复选取。

代码:

#python
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        res, path = [], []
        if not candidates:
            return res
        
        def dfs(i):
            if sum(path) == target:
                res.append(path.copy())
                return 
     
            for j in range(i, len(candidates)): // 和上一题一样,也要用下标来固定元素不能重复出现。
                if j > i and candidates[j] == candidates[j-1]: //相同就继续,别再选了
                    continue
                if sum(path) + candidates[j] > target:  //超过范围了,因为是有序的,后面别选了
                    break
                path.append(candidates[j])
                dfs(j+1)
                path.pop()
        
        candidates.sort()
        dfs(0)
        return res
/java
class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates); // 对数组进行排序
        List<List<Integer>> res = new ArrayList<>();
        backtrack(candidates, target, 0, new ArrayList<>(), res, new boolean[candidates.length]);
        return res;
    }

    private void backtrack(int[] candidates, int target, int start, List<Integer> path, List<List<Integer>> res, boolean[] used) {
        if (target == 0) {
            res.add(new ArrayList<>(path)); // 找到一种组合
            return;
        }

        for (int i = start; i < candidates.length; i++) {
            // 跳过重复的元素
            if (i > start && candidates[i] == candidates[i - 1]) {
                continue;
            }
            // 如果当前元素大于剩余目标,直接返回
            if (candidates[i] > target) {
                break;
            }
            // 使用当前元素
            if (!used[i]) {
                used[i] = true; // 标记为已使用
                path.add(candidates[i]); // 添加到路径
                backtrack(candidates, target - candidates[i], i + 1, path, res, used); // 递归调用
                path.remove(path.size() - 1); // 回溯,移除当前元素
                used[i] = false; // 重置为未使用
            }
        }
    }
}

LeetCode 131. 分割回文串
题目链接:131. 分割回文串 - 力扣(LeetCode)

​思路:

与之前题目思路是相同的,只不过判断条件变为了从下标0一直到长度n结束,把中间所有的回文字符串均加入path中,最后把所有的res给return。

代码:

#python 
class Solution:
    def partition(self, s: str) -> List[List[str]]:
        n = len(s)
        res, path = [], []
        if n == 0:
            return res
        def dfs(i):
            if i == n:
                res.append(path.copy())
                return
            for j in range(i, n + 1):
                t = s[i : j + 1]   //利用字符串操作来分割
                if t == t[::-1]:   //判断
                    path.append(t)
                    dfs(j + 1)
                    path.pop()
        dfs(0)
        return res
/java
public class Solution {  
  
    List<List<String>> result = new ArrayList<>();  
  
    public List<List<String>> partition(String s) {  
        List<String> path = new ArrayList<>();  
        backtrack(s, 0, path);  
        return result;  
    }  
  
    private void backtrack(String s, int start, List<String> path) {  
        if (start == s.length()) {  
            result.add(new ArrayList<>(path));  
            return;  
        }  
  
        for (int i = start; i < s.length(); i++) {  
            if (isPalindrome(s, start, i)) {  
                String palindrome = s.substring(start, i + 1);  
                path.add(palindrome);  
                // 继续递归处理剩余部分  
                backtrack(s, i + 1, path);  
                // 回溯,撤销选择  
                path.remove(path.size() - 1);  
            }  
        }  
    }  
  
    private boolean isPalindrome(String s, int left, int right) {  
        while (left < right) {  
            if (s.charAt(left) != s.charAt(right)) {  
                return false;  
            }  
            left++;  
            right--;  
        }  
        return true;  
    }  
  

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

别看了真C不了一点

打赏者皆为义父

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值