代码随想录算法训练营day23

代码随想录算法训练营

---day23

文章目录


前言

今天是算法营的第23天,希望自己能够坚持下来!

今日任务:

● 39. 组合总和

● 40.组合总和II

● 131.分割回文串


一、39. 组合总和

题目链接
文章讲解
视频讲解

思路:

  1. 递归函数的参数和返回值:参数:整数数组 candidates,目标整数 target,总和sum,遍历的开始索引startIndex。
  2. 终止条件:本题对path的大小没有限制,只有总和的限制,当总和=target的时候结束,将path放入结果集。
  3. 单层递归的逻辑:纵向遍历可以重复使用数字,所以for循环中递归用的是i(若不重复则是i+1)。
  4. 去重:先将数组排序,如果sum+candidates[i]>target那么后面的元素都会大于target,所以for循环的终止条件可以加上这个,提前退出循环。
cpp 复制代码
class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;

    void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {
        if (sum == target) { //总和等于目标值则加入结果集
            result.push_back(path);
            return;
        }

        //如果sum+candidates[i]>target就终止遍历
        for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i ++) {
            sum += candidates[i];
            path.push_back(candidates[i]);
            backtracking(candidates, target, sum, i); //这里因为同一个数字可以重复使用,所以用startIndex是i
            sum -= candidates[i]; //回溯
            path.pop_back(); //回溯
        }

        return;
    }

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        path.clear();
        result.clear();
        sort(candidates.begin(), candidates.end()); //需要排序
        if (candidates.size() == 0) return result;
        backtracking(candidates, target, 0, 0);
        return result;
    }
};

二、40.组合总和II

题目链接
文章讲解
视频讲解

这道题目和39.组合总和 (opens new window)如下区别:

1.本题candidates 中的每个数字在每个组合中只能使用一次。

2.本题数组candidates的元素是有重复的,而39.组合总和 (opens new window)是无重复元素的数组candidates

最后本题和39.组合总和 (opens new window)要求一样,解集不能包含重复的组合。

本题的难点在于区别2中:集合(数组candidates)有重复元素,但还不能有重复的组合。

本题是多加了一个used数组,去记录纵向遍历时使用过的数字。

树层去重的话,需要对数组排序!

思路:

  1. 递归函数参数以及返回值:参数数组candidates,目标值target,总和sum,索引startIndex,标记使用过的数字数组used。
  2. 终止条件:sum == target
  3. 单层处理逻辑:当前元素与前一位元素相等,且是前一位元素没用过,也就是以当前元素(重复元素)开始寻找新的结果,跳过这次循环

代码如下:

cpp 复制代码
class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;

    void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used)
    {
        if (sum == target) {
            result.push_back(path);
            return;
        }

        for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i ++) {
            //当前元素与前一位元素相等,且是前一位元素没用过,也就是以当前元素开始寻找新的结果,跳过这次循环
            if (i > 0 && candidates[i] == candidates[i-1] && used[i-1] == false) continue; //去重

            path.push_back(candidates[i]);
            sum += candidates[i];
            used[i] = true; //记录使用当前结果path用了那些元素
            backtracking(candidates, target, sum, i + 1, used); //candidates里的数字只能用一次,i+1
            sum -= candidates[i];
            path.pop_back();
            used[i] = false;
        }
    }

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<bool> used (candidates.size(), false);
        path.clear();
        result.clear();

        //需要把candidates排序,让相同的元素挨在一起
        sort(candidates.begin(), candidates.end());
        if (candidates.size() == 0) return result;
        backtracking(candidates, target, 0, 0, used);
        return result;
    }
};

三、131.分割回文串

题目链接
文章讲解
视频讲解

跟组合问题类似,递归遍历每个字符串的位置去切割,每一种结果都是一种多个切割点的组合。只是每次切割都需要判断切割后是否为回文字符子串,是则放入path,继续切割,否则继续遍历寻找合适的切割点。

思路:

  1. 递归函数的参数:传入字符串s,遍历索引startIndex
  2. 终止条件:startIndex已经到达字符串尾端(将判断回文放在了for循环中,这里直接放入结果集)
  3. 单层递归的逻辑:[startIndex,i]就是本次截取的字符串,判断其是否回文,是则加入path中,否则continue去寻找合适的切割点。递归寻找i+1为起始位置的子串。
cpp 复制代码
class Solution {
public:
    vector<string> path; //放已经回文的子串
    vector<vector<string>> result;

    void backtracking(string& s, int startIndex) {
        if (startIndex >= s.size()) {
            result.push_back(path);
            return;
        }

        for (int i = startIndex; i < s.size(); i++) {
            if (isPalindrome(s, startIndex, i)) {  //判断[startIndex,i]之间的字符串是否为回文子串
                //是则截取出来,放入path
                string str = s.substr(startIndex, i - startIndex + 1); //substr是左闭右开区间所以+1
                path.push_back(str);
            } else {
                continue; //不是回文则跳过
            }
            backtracking(s, i + 1); //递归寻找i+1为起始位置的子串
            path.pop_back(); //回溯
        }
    }

    //双指针判断是否为回文子串
    bool isPalindrome(const string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            if (s[i] != s[j]) return false;
        }
        return true;
    }

    vector<vector<string>> partition(string s) {
        result.clear();
        path.clear();

        if (s.size() == 0) return result;
        backtracking(s, 0);
        return result;
    }
};

总结

1.今天第一次涉及到去重,去重又分树枝(纵向)去重和树层(横向)去重。可以使用used数组去标记使用过的元素。

2.分割问题跟组合问题类似,也是求不同分割点的组合。在for循环中判断是否符合切割要求。

明天继续加油!

相关推荐
飞yu流星1 小时前
C++ 函数 模板
开发语言·c++·算法
pursuit_csdn1 小时前
力扣 74. 搜索二维矩阵
算法·leetcode·矩阵
labuladuo5201 小时前
洛谷 P8703 [蓝桥杯 2019 国 B] 最优包含(dp)
算法·蓝桥杯·动态规划
Goldinger1 小时前
vscode 配置c/c++环境 中文乱码
c语言·c++·vscode
Milk夜雨2 小时前
C语言冒泡排序教程简介
数据结构·算法·排序算法
nSponge2 小时前
【Duilib】 List控件支持多选和获取选择的多条数据
c++·windows·工具
委婉待续2 小时前
redis的学习(三)
数据结构·算法
一直学习永不止步2 小时前
LeetCode题练习与总结:随机翻转矩阵--519
java·数学·算法·leetcode·哈希表·水塘抽样·随机化
Y Shy2 小时前
Windows C++开发环境:VSCode + cmake + ninja + msvc (cl.exe) + msys2/bash shell
c++·windows·vscode·msvc·cmake·msys2·ninja
xiao--xin2 小时前
LeetCode100之组合总和(39)--Java
java·开发语言·算法·leetcode·回溯