day23|leetCode 39. 组合总和 , 40.组合总和II , 131.分割回文串

5.组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

对比一下:

找出所有相加之和为 nk 个数的组合,且满足下列条件:

  • 只使用数字1到9

  • 每个数字 最多使用一次

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

其实我觉得这两题挺像的,最大的区别就是第五题并没有k的限制,所以深度并未被限制

以及元素可以重复选用,所以backtracking(target,candidates,i)传入的应该是i而不是i+1,这样才能保证结果中可以出现重复元素的结果

cpp 复制代码
class Solution {
public:
    vector<vector<int>>result;
    vector<int>path;
    void backtracking(vector<int>&candidates,int target,int sum,int startIndex)
    {
        
        //终止条件
        if(sum>target)//一定加上这个判断,不然就会栈溢出
        {
            return;
        }
        if(sum==target)
        {
            result.push_back(path);
            return;
        }
        for(int i = startIndex;i<candidates.size();i++)//注意本题的i是坐标
        {
           sum+=candidates[i];
           path.push_back(candidates[i]);
           backtracking(candidates,target,sum,i);
           path.pop_back();
           sum-=candidates[i];
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        result.clear();
        path.clear();
        backtracking(candidates,target,0,0);//startIndex在本题传入的是下标,所以从0开始
        return result;
    }
};

6.组合总和II

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次

**注意:**解集不能包含重复的组合。

这题更像了

找出所有相加之和为 nk 个数的组合,且满足下列条件:

  • 只使用数字1到9

  • 每个数字 最多使用一次

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

最大的区别就是去重!

要求是不可在一个组合中使用用一个元素,但是可以用值相等的不同元素!

复制代码
首先,我们在给backtracking传入数组时,我们不是传入原来的数组,而是将排序过后的数组传入 
    为什么呢?
示例:输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
如果直接操作原数组,那么在取第一个1的时候,我们会得到一次[1,7],然后在取第二个1的时候,又会得到一次[1,7]就重复了
但是如果我们将数组进行排序,并引入一个bool类型的used数组,这个数组的长度与数组相同,每个位置的false代表这个位置对应的元素已经使用过,如果是true说明这个元素正在使用
排序后的数组:[1,1,2,5,6,7,10]起到将相邻的元素放到一起的作用
然后传入 backtracking(),当循环到第一个1时,将数组used[i]设为true,代表正在历经第一个1能组成的所有结果,遍历完就把used设回false
再循环到第二个1时,通过判断candidates[i]是否与candidates[i-1]相等 && used[i-1]是否为false,如果相等并且used[i-1]=false说明当前这个和前者相同的元素已经遍历完了所有它的可能值,不要再循环了

难点讲解完毕,上代码

cpp 复制代码
class Solution {
public:
    vector<vector<int>>result;
    vector<int>path;
    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++)
        {//注意:sum+candidates[i]<=target是剪枝操作
            if(i>0 && candidates[i]==candidates[i-1] && used[i-1]==false)//说明这个元素用过了
            {
                continue;
            }
            sum+=candidates[i];
            path.push_back(candidates[i]);
            used[i]=true;//正在使用
            backtracking(candidates,target,sum,i+1,used);
            used[i]=false;//用过了
            path.pop_back();//回溯
            sum-=candidates[i];//
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<bool>used(candidates.size(),false);//设置used数组来管理使用过的元素
        result.clear();
        path.clear();
        sort(candidates.begin(),candidates.end());//难点,为什么要给数组排序
        backtracking(candidates,target,0,0,used);
        return result;
    }
};

7.分割回文子串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是

回文串

。返回 s 所有可能的分割方案。

示例 1:

复制代码
输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例 2:

复制代码
输入:s = "a"
输出:[["a"]]

上图是根据给的示例做的分割,不理解的话建议看卡哥的视频讲解,听完就感觉这题很简单了

套用回溯算法模板

cpp 复制代码
void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }
​
    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}
cpp 复制代码
class Solution {
public:
    vector<string>path;//存储每一组回文串的集合
    vector<vector<string>>result;//二维数组,存储最终结果
    bool isHuiWen(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;
    }
​
    void backtracking(const string& s,int startIndex)//startIndex起到切割线的作用
    {//终止条件
        if(startIndex >= s.size())//说明已经切割到字符串最后
        {
            result.push_back(path);
            return;
        }
        for(int i = startIndex;i<s.size();i++)
        {//startIndex是不变的,但是i是不断增加的
            //难点:要想到边循环边判断回文
            if(isHuiWen(s,startIndex,i)==true){
                //取子串
                string str = s.substr(startIndex,i-startIndex+1);
                path.push_back(str);
            }else{
                continue;
            }
          backtracking(s,i+1);
          path.pop_back();//回溯弹出
        }
    }
    vector<vector<string>> partition(string s) {
        result.clear();
        path.clear();
        backtracking(s,0);
        return result;
    }
};
​
时间复杂度O(n²)
相关推荐
daiyang123...15 分钟前
Java 复习 【知识改变命运】第九章
java·开发语言·算法
fancc椰19 分钟前
C++基础入门篇
开发语言·c++
田梓燊1 小时前
湘潭大学软件工程算法设计与分析考试复习笔记(六)
笔记·算法·软件工程
晚安,cheems1 小时前
c++(入门)
开发语言·c++
重生之Java开发工程师1 小时前
算法笔记:前缀和
笔记·算法
sweetheart7-71 小时前
LeetCode155. 最小栈(2024冬季每日一题 12)
算法··模拟栈·最小元素
藏鹤虞渊2 小时前
【ONE·基础算法 || 动态规划(二)】
算法·动态规划
人才程序员2 小时前
详解Qt QStorageInfo 存储信息类
c语言·开发语言·c++·后端·qt·界面
ZHOUPUYU2 小时前
最新‌VSCode保姆级安装教程(附安装包)
c语言·开发语言·c++·ide·windows·vscode·编辑器