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

本文介绍了如何使用回溯算法解决组合总和问题。针对三个具体的实例,详细阐述了解题思路,包括如何确定递归函数参数、终止条件以及单层搜索逻辑。解题过程中涉及的三个问题分别是:寻找数组中所有和为目标数的组合、限制元素只使用一次的组合以及分割回文串。通过递归和剪枝策略,实现了高效求解这些问题的解决方案。
摘要由CSDN通过智能技术生成

39. 组合总和

题目描述:

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 对于给定的输入,保证和为 target 的不同组合数少于 150 个。

解题思路:

回溯三部曲:

①确定递归函数的参数和返回值:首先定义两个全局变量,result作为二维数组来存放最终结果,path作为一维数组来存放遍历时符合条件的路径。题目中给出的candidates和target可以作为参数传入,除此之外,还需要定义一个int类型的sum用来存放单个路径上的一个总和。

同时,本题还需要一个startIndex来控制for循环的起止位置。

②递归终止条件:终止条件有两种情况,一种是target==sum,另一种是target > sum.当达到这两种情况,条件终止。

③确定单层搜索逻辑:单层for循环依旧是从startIndex开始,横向搜索candidates集合。

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

    public void backtracking(List<List<Integer>> res,List<Integer> path,int[] candidates, int target,int sum, int idx){
        if(sum == target){
            res.add(new ArrayList<>(path));
            return;
        }

        for(int i = idx;i < candidates.length;i++){
            if(sum + candidates[i] > target) break;
            path.add(candidates[i]);
            backtracking(res,path,candidates, target,sum + candidates[i],i);
            path.remove(path.size() - 1);
        }
    }
}

40. 组合总和 II

 题目描述:

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。说明: 所有数字(包括目标数)都是正整数。 解集不能包含重复的组合。

示例 1: 输入: candidates = [10,1,2,7,6,1,5], target = 8, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]

示例 2: 输入: candidates = [2,5,2,1,2], target = 5, 所求解集为: [   [1,2,2],   [5] ]

解题思路:

回溯三部曲:

①递归函数参数: 需要加一个bool型数组used,用来记录同一树枝上的元素是否使用过。这个集合去重的重任就是used来完成的。剩下的参数与上一题的参数相类似。

②递归终止条件:终止条件为 sum > target 和 sum == target

③单层搜索的逻辑:要去重的是“同一树层上的使用过”;如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。此时for循环里就应该做continue的操作。

40.组合总和II1

  • used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
  • used[i - 1] == false,说明同一树层candidates[i - 1]使用过
class Solution {
    List<List<Integer>>  result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    boolean []used;
    int sum = 0;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        used = new boolean[candidates.length];
        Arrays.fill(used,false);
        Arrays.sort(candidates);
        backtracking(candidates,target,0);
        return result;
    }
    private void backtracking(int []candidates,int target,int startIndex){
        if(sum == target){
            result.add(new ArrayList<>(path));
        }
        for(int i = startIndex; i < candidates.length; i++){
            if(sum + candidates[i] > target){
                break;
            }
            if(i > 0 && candidates[i] == candidates[i-1] && !used[i -1]){
                continue;
            }
            used[i] = true;
            sum += candidates[i];
            path.add(candidates[i]);
            backtracking(candidates,target,i + 1);
            used[i] = false;
            sum -= candidates[i];
            path.removeLast();
        }
    }
}

131. 分割回文串

题目描述: 给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。

解题思路:

回溯三部曲:

①确定递归函数返回的参数:全局变量path存放切割过后的字串,二维数组存放结果集(这两个参数均可以放到函数的参数里),除此之外,还需要一个参数为startIndex,为了确定每次切割的位置,因为已经切割的位置不可以重复切割。

②递归函数的终止条件:

 代码随想录

 当切割符切割到字符串的最后面,说明程序已经找到了切割方法,此时就是本层递归的终止条件。在本代码中,startIndex就是切割线。

③单层递归逻辑:在for(int i = startIndex; i < s.length;i++)循环中定义了起始位置startIndex,那么[startIndex,i]就是我们要找的字符串。然后判断这个字串是不是回文串,如果是回文串就放在path集里面,path用来记录切割过的回文子串。注意切割过的位置,不能重复切割,所以,backtracking(s, i + 1); 传入下一层的起始位置为i + 1。

 如何判断一个字符串是否是回文子串,可以使用双指针法,一个指针从前向后,一个指针从后向前,如果两个指针对应的元素都相等,则为回文子串。

class Solution {
    List<List<String>> lists = new ArrayList<>();
    Deque<String> deque = new LinkedList<>();

    public List<List<String>> partition(String s) {
        backTracking(s, 0);
        return lists;

    }
    public void backTracking(String s,int startIndex){
        if(startIndex >= s.length()){
            lists.add(new ArrayList(deque));
            return;
        }
        for(int i = startIndex; i < s.length(); i++){
            if (isPalindrome(s, startIndex, i)){
                String str = s.substring(startIndex,i+1);
                deque.addLast(str);
            }else{
                continue;
            }
            // 起始位置后移,保证不重复
            backTracking(s, i + 1);
            deque.removeLast();
        }
    }
    private boolean isPalindrome(String s, int startIndex, int end) {
        for (int i = startIndex, j = end; i < j; i++, j--) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }
}

 

代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值