回溯法刷题ing

##回溯法##

回溯法总结

1.DFS和回溯算法区别

DFS是一个劲的往某一个方向搜索,而回溯算法建立在DFS基础之上的,但不同的是在搜索过程中,达到结束条件后,恢复状态,回溯上一层,再次搜索。因此回溯算法与DFS的区别就是有无状态重置

2.何时使用回溯算法

** 当问题需要"回头",以此来查找出所有的解的时候**,使用回溯算法。即满足结束条件或者发现不是正确路径的时候(走不通),要撤销选择,回退到上一个状态,继续尝试,直到找出所有解为止

3.怎么样写回溯算法(从上而下,※代表难点)

①画出递归树,找到状态变量(回溯函数的参数),这一步非常重要※
②根据题意,确立结束条件
③找准选择列表(与函数参数相关),与第一步紧密关联※
④判断是否需要剪枝
⑤作出选择,递归调用,进入下一层
⑥撤销选择

4.回溯问题的类型

类型题目链接
子集、组合子集子集 II组合组合总和组合总和 II
全排列全排列全排列 II字符串的全排列字母大小写全排列
搜索解数独单词搜索N皇后分割回文串二进制手表

4.1子集、组合

子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

**说明:**解集不能包含重复的子集。

输入: nums = [1,2,3]
输出:
[
[3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
class Solution {
public:
    vector<vector<int>> res;
    void backtrack(vector<int>& nums,vector<int>& path,int index){
        res.push_back(path);
        for (int i = index; i <nums.size() ; ++i) {
            path.push_back(nums[i]);
            backtrack(nums,path,i+1);
            path.pop_back();
        }
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<int> path{};
        backtrack(nums,path,0);
        return res;
    }
};

子集2

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

**说明:**解集不能包含重复的子集。

输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]

加了剪枝

class Solution {
public:
    vector<vector<int>> res;
    void backtrack(vector<int>& nums,vector<int>& path,int index){
        res.push_back(path);
        for (int i = index; i <nums.size() ; ++i) {
            if(i>index && nums[i-1]==nums[i])
                continue;
            path.push_back(nums[i]);
            backtrack(nums,path,i+1);
            path.pop_back();
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<int> path{};
        backtrack(nums,path,0);
        return res;
    }
};

组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]

输入: candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]
class Solution {
public:
    vector<vector<int>> res;
    void dfs(vector<int>& candidates,vector<int>& path, int index,int sum,int target){
        if(sum==target){
            res.push_back(path);
            return;
        }
        for (int i = index; i <candidates.size() ; ++i) {
            if(sum>target)
                continue;
            path.push_back(candidates[i]);
            dfs(candidates,path,i,sum+candidates[i],target);
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int> path;
        dfs(candidates,path,0,0,target);
        set<vector<int>> temp(res.begin(),res.end());
        res.assign(temp.begin(),temp.end());
        return res;
    }
};

分割回文串 嘿嘿

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

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

输入: "aab"
输出:
[
["aa","b"],
["a","a","b"]
]
class Solution {
public:
    vector<vector<string>> res;
    void backtrack(string s,vector<string>& parts,int index){
        if(index==s.size()){
            res.push_back(parts);
            return;
        }
        string temp1;
        for (int i = index; i <s.size() ; ++i) {
            temp1+=s[i];
            string temp2=temp1;
            reverse(temp2.begin(),temp2.end());
            if(temp2==temp1){
                parts.push_back(temp1);
                backtrack(s,parts,i+1);
                parts.pop_back();
            }
        }
    }
    vector<vector<string>> partition(string s) {
        vector<string> parts;
        backtrack(s,parts,0);
        return res;
    }
};

电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e1XhLRhc-1595485861554)(C:\Users\86178\Desktop\力扣\图片\2020-05-21_213228.png)]

自己写的
class Solution {
public:
    unordered_map<char,string> map{
            {'2',"abc"},{'3',"def"},{'4',"ghi"},{'5',"jkl"},{'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"}
    };
    vector<string> res;

    void tracbeck(string digits,int index,string& path){
        if(index==digits.size()){
            if(path.size()==digits.size())
                res.push_back(path);
            return;
        }
        string words;
        for (int i = index; i <digits.size() ; ++i) {
            words=map[digits[index]];
            for (int j = 0; j < words.size(); ++j) {
                path+=words[j];
                tracbeck(digits,i+1,path);
                path.erase(path.end()-1);
            }
        }
    }
    vector<string> letterCombinations(string digits) {
        if(digits=="")
            return res;
        string path;
        tracbeck(digits,0,path);
        return res;
    }
};
看了题解优化了
class Solution {
public:
    unordered_map<char,string> map{
            {'2',"abc"},{'3',"def"},{'4',"ghi"},{'5',"jkl"},{'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"}
    };
    vector<string> res;
    string path;

    void tracbeck(string digits,int index){
        if(index==digits.size()){
            res.push_back(path);
            return;
        }
        for (int j = 0; j < map[digits[index]].size(); ++j) {
            path.push_back(map[digits[index]][j]);
            tracbeck(digits,index+1);
            path.pop_back();
        }
    }
    vector<string> letterCombinations(string digits) {
        if(digits.size()==0)
            return res;
        tracbeck(digits,0);
        return res;
    }
};

无重复字符串的排列组合

无重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合,字符串每个字符均不相同。

输入:S = "qwe"
输出:["qwe", "qew", "wqe", "weq", "ewq", "eqw"]
标准回溯
class Solution {
public:
    vector<string> res;
    void backtrack(string S,string& path,vector<int>& flag,int index){
       if(index==S.size()){
           res.push_back(path);
           return;
       }
        for (int i = 0; i < S.size(); ++i) {
            if(flag[i])
                continue;
            path.push_back(S[i]);
            flag[i]=1;
            backtrack(S,path,flag,index+1);
            path.pop_back();
            flag[i]=0;
        }
    }
    vector<string> permutation(string S) {
        string path;
        vector<int> flag(S.size(),0);
        backtrack(S,path,flag,0);
        return res;
    }
};
回溯2
class Solution {
public:
    vector<string> res;
    void backtrack(string S,int index){
        if(index==S.size()){
            res.push_back(S);
            return;
        }
        for (int i = index; i <S.size() ; ++i) {
            string temp=S;
            swap(temp[index],temp[i]);
            backtrack(temp,index+1);
        }
    }
    vector<string> permutation(string S) {
        string path;
        backtrack(S,0);
        return res;
    }
};

无重复字符串的排列组合2

有重复字符串的排列组合

使用set去重
class Solution {
public:
    set<string> res;
    void backtrack(string S,string& path,vector<int>& flag){
       if(path.size()==S.size()){
           res.insert(path);
           return;
       }
        for (int i = 0; i < S.size(); ++i) {
            if(flag[i]==0){
                path.push_back(S[i]);
                flag[i]=1;
                backtrack(S,path,flag);
                path.pop_back();
                flag[i]=0;
            }
        }
    }
    vector<string> permutation(string S) {
        string path;
        vector<int> flag(S.size(),0);
        backtrack(S,path,flag);
        vector<string> temp(res.begin(),res.end());
        return temp;
    }
};
剪枝去重

思路

多了一个排序和剪枝函数
排序使得重复的在一起
剪枝讲解:
1、因为i-1的存在 所以i>0
2、S[i]==S[i-1] && flag[i-1]==0表示这个i的子树i-1都经历过了 且是刚经历
	在递归树中可以看出这个特点 
if(i>0 && S[i]==S[i-1] && flag[i-1]==0)
             continue;
class Solution {
public:
    vector<string> res;
    void backtrack(string S,string& path,vector<int>& flag,int index){
        if(index==S.size()){
            res.push_back(path);
            return;
        }
        for (int i = 0; i < S.size(); ++i) {
            if(flag[i])
                continue;
            if(i>0 && S[i]==S[i-1] && flag[i-1]==0)
                continue;
            path.push_back(S[i]);
            flag[i]=1;
            backtrack(S,path,flag,index+1);
            path.pop_back();
            flag[i]=0;
        }
    }
    vector<string> permutation(string S) {
        string path;
        vector<int> flag(S.size(),0);
        sort(S.begin(),S.end());
        backtrack(S,path,flag,0);
        return res;
    }
};

4.2搜索

二进制手表顶部有 4 个 LED 代表小时(0-11),底部的 6 个 LED 代表分钟(0-59)

每个 LED 代表一个 0 或 1,最低位在右侧。

给定一个非负整数 n 代表当前 LED 亮着的数量,返回所有可能的时间。

输入: n = 1
返回: ["1:00", "2:00", "4:00", "8:00", "0:01", "0:02", "0:04", "0:08", "0:16", "0:32"]

注意事项:

输出的顺序没有要求。
小时不会以零开头,比如 “01:00” 是不允许的,应为 “1:00”。
分钟必须由两位数组成,可能会以零开头,比如 “10:2” 是无效的,应为 “10:02”。

思路:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rd0rcmW5-1595485861557)(C:\Users\86178\Desktop\力扣\图片\2020-05-20_144911.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iv6xggWh-1595485861558)(C:\Users\86178\Desktop\力扣\图片\2020-05-20_144926.png)]

class Solution {
public:
    vector<string> res;
    unordered_map<int ,int> hash={
            {0,1},{1,2},{2,4},{3,8},{4,1},{5,2},{6,4},{7,8},{8,16},{9,32}
    };
    void backtrak(int num,int index,pair<int,int>& time){
        if(num==0){
            if(time.first>11 || time.second>59)
                return;
            string hour=to_string(time.first);
            string minute=to_string(time.second);
            if(minute.size()==1)
                minute.insert(0,"0");
            res.push_back(hour+":"+minute);//构造格式
        }
        for (int i = index; i <10 ; ++i) {
            if(time.first>11 || time.second>59)
                continue;
            pair<int,int> temp=time;//保存状态
            if(i<4)
                time.first+=hash[i];
            else
                time.second+=hash[i];
            backtrak(num-1,i+1,time);//进入下一层,注意下一层的start是i+1,即从当前灯的下一盏开始
            time=temp;//恢复状态
        }
    }
    vector<string> readBinaryWatch(int num) {
        pair<int,int> time(0,0);
        backtrak(num,0,time);
        return res;
    }
};

22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例:

输入:n = 3
输出:[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]

深搜

class Solution {
public:
    vector<string> res;
    void dfs(string path, int n, int lc, int rc){
        if(rc>lc || lc>n || rc>n) return;
        if(lc==rc && lc==n){
            res.push_back(path);
            return;
        }
        dfs(path + '(', n, lc + 1, rc);
        dfs(path + ')', n, lc, rc + 1);
    }
    vector<string> generateParenthesis(int n) {
        int lc=0;//左括号个数
        int rc=0;//右括号个数
        string path="";
        dfs(path, n, lc, rc);
        return res;
    }
};

回溯

class Solution {
public:
    vector<string> res;
    void backtrack(string& path,int n,int lc,int rc){
        if(path.size()==n*2){
            res.push_back(path);
            return;
        }
        if(lc<n){
            path.push_back('(');
            backtrack(path,n,lc+1,rc);
            path.pop_back();
        }
        if(rc<lc){
            path.push_back(')');
            backtrack(path,n,lc,rc+1);
            path.pop_back();
        }
    }
    vector<string> generateParenthesis(int n) {
        int lc=0;//左括号个数
        int rc=0;//右括号个数
        string path="";
        backtrack(path,n,lc,rc);
        return res;
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值