算法日志8:百特热达斯特

前言

本文为回溯法刷题记录

如何理解回溯法
回溯法解决的问题都可以抽象为树形结构,是的,我指的是所有回溯法的问题都可以抽象为树形结构!

因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度就构成了树的深度。

递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。

回溯三部曲

  1. 函数入参和出参
    回溯算法中函数返回值一般为void。
  2. 递归终止条件
  3. 单层递归的逻辑

组合

class Solution {
public:
    //函数和返回值参数
    vector<vector<int>> res;
    vector<int> path;
    void getres(int n, int k, int l){
        //终止条件
        if(path.size() == k){
            res.push_back(path);
            return;
        }

        //单层逻辑
        for(int i = l; i <= n;i++){
            path.push_back(i);
            getres(n, k, i+1);
            path.pop_back();
        }

    }
    vector<vector<int>> combine(int n, int k) {
        getres(n,k,1);
        return res;
    }
};

组合(剪枝)

class Solution {
public:
    //函数和返回值参数
    vector<vector<int>> res;
    vector<int> path;
    void getres(int n, int k, int l){
        //终止条件
        if(path.size() == k){
            res.push_back(path);
            return;
        }
        /*
        接下来看一下优化过程如下:

        已经选择的元素个数:path.size();

        所需需要的元素个数为: k - path.size();

        列表中剩余元素(n-i) >= 所需需要的元素个数(k - path.size())

        在集合n中至多要从该起始位置 : i <= n - (k - path.size()) + 1,开始遍历

        为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。

        举个例子,n = 4,k = 3, 目前已经选取的元素为0(path.size为0),n - (k - 0) + 1 即 4 - ( 3 - 0) + 1 = 2。
        
        */
        //单层逻辑
        for(int i = l; i <= n-k+path.size()+1;i++){
            path.push_back(i);
            getres(n, k, i+1);
            path.pop_back();
        }

    }
    vector<vector<int>> combine(int n, int k) {
        getres(n,k,1);
        return res;
    }
};

组合综合III

普通

class Solution {
public:
    //入参出参
    vector<vector<int>> res;
    vector<int> path;
    int sum = 0;
    void bfs(int len, int tar, int l){
        //终止条件
        if(path.size() == len){
            if(sum == tar) res.push_back(path);
            return;
        }
        //单层逻辑
        for(int i = l; i <= 9;i++){
            path.push_back(i);
            sum+=i;
            bfs(len, tar, i+1);
            path.pop_back();
            sum-=i;
        }
    }
    vector<vector<int>> combinationSum3(int k, int n) {
        bfs(k,n,1);
        return res;
    }
};

剪枝

class Solution {
public:
    //入参出参
    vector<vector<int>> res;
    vector<int> path;
    int sum = 0;
    void bfs(int len, int tar, int l){
        //终止条件
        if(path.size() == len){
            if(sum == tar) res.push_back(path);
            return;
        }
        // 9 - i传入下一层大小
        // len - path.size()剩余层数
        // 9-i>=len- path.size() ==> i<=9-len+path.size()
        // 然后再加i <= 9-len+path.size()+1
        //单层逻辑
        for(int i = l; i <= 9-len+path.size()+1;i++){
            path.push_back(i);
            sum+=i;
            bfs(len, tar, i+1);
            path.pop_back();
            sum-=i;
        }
    }
    vector<vector<int>> combinationSum3(int k, int n) {
        bfs(k,n,1);
        return res;
    }
};

电话号码的字母组合

将字符转为字符串

下面的代码:

vector<string> arr;
arr.push_back(""+'a');

表达式 "" + 'a' 的含义:

"" 是一个空的字符串字面量(类型为 const char[1]).
'a' 是一个字符(类型为 char).
在 C++ 中,"" + 'a' 实际上是将 'a' 的 ASCII 值(97)作为偏移量加到 "" 的地址上。这会导致生成一个指向无效内存的指针,行为未定义。

如果想要将'a'转为字符串类型, 要写string("") + 'a',

to_string('a')也是不行的!,to_string只能用于数字,如果使用to_string('a') 会得到'a'的ASCII 值构成的字符串,即得到"97"

所以,在c++中,如果想把字符型数据转为字符串,有如下方法

使用 std::string(1, c) 构造字符串。
使用字符串拼接(std::string("") + c)。
使用 std::stringstream

将整型、浮点型数据转换为字符串

使用 std::to_string
C++11 引入了 std::to_string 函数,可以直接将数值类型(如 int、float、double 等)转换为字符串。

int i = 123;
float f = 45.67f;
double d = 89.123456;

std::string str_i = std::to_string(i);  // 整型转字符串
std::string str_f = std::to_string(f);  // 浮点型转字符串
std::string str_d = std::to_string(d);  // 双精度浮点型转字符串

将字符串转换为整型、浮点型数据

使用 std::stoi、std::stof、std::stod

C++11 提供了标准库函数,用于将字符串转换为数值类型:

std::stoi:字符串转整型。
std::stof:字符串转单精度浮点型。
std::stod:字符串转双精度浮点型。

std::string str_i = "123";
std::string str_f = "45.67";
std::string str_d = "89.123456";

int i = std::stoi(str_i);       // 字符串转整型
float f = std::stof(str_f);     // 字符串转浮点型
double d = std::stod(str_d);    // 字符串转双精度浮点型

题解

class Solution {
public:
    //入参出参
    vector<string> map = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    string path;
    vector<string> res;
    void bfs(string digits, int cur, int len){
        //终止条件
        if(cur == len){
            res.push_back(path);
            return;
        }
        //单层逻辑
        string s = map[(int)(digits[cur]-'0')];
        for(int i = 0 ; i< s.size();i++){
            path+=s[i];
            bfs(digits, cur+1, len);
            path.pop_back();
        }

    }
    vector<string> letterCombinations(string digits) {
        if(digits == "") return res;
        bfs(digits,0, digits.size());
        return res;
    }
};

题解2:

class Solution {
public:
    //入参出参
    vector<string> map = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    vector<string> path;
    vector<string> res;
    void bfs(string &digits, int cur, int len){
        //终止条件
        if(cur == len){
            string tmp ="";
            for(auto i:path){
                tmp+=i;
            }
            res.push_back(tmp);
            return;
        }
        //单层逻辑
        string s = map[(int)(digits[cur]-'0')];
        for(int i = 0 ; i< s.size();i++){
            path.push_back(string("")+s[i]);
            bfs(digits, cur+1, len);
            path.pop_back();
        }

    }
    vector<string> letterCombinations(string digits) {
        if(digits == "") return res;
        bfs(digits,0, digits.size());
        return res;
    }
};

组合总和

错误代码

class Solution {
public:
//出参入参
    vector<vector<int>> res;
    vector<int> path;
    int sum = 0;
    void bfs(vector<int>& arr, int tar){
        //终止条件
        if(sum > tar) return;
        if(sum == tar){
            res.push_back(path);
            return;
        }
        //单层逻辑
        for(auto elem:arr){
            path.push_back(elem);
            sum+=elem;
            bfs(arr, tar);
            sum-=elem;
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        bfs(candidates,target);
        return res;
    }
};

错误原因分析

例如

candidates = [2,3,6,7]
target = 7

如果使用上面的代码,最后的结果是
[[2,2,3],[2,3,2],[3,2,2],[7]]
发现有重复的,[2,3,2] 和 [3,2,2]都和[2,2,3]重复了,
所以我们这样取数就可以
img

情况A: 第一次取2,下一次可以取[2,3,6,7]
情况B: 第一次取3,下一次就取[3,6,7]
情况B中,下一次取数就不需要再取2了,因为假设第一次取3,下一次取2,这种情况其实在情况A的遍历过程中已经出现了,只不过情况B此种情况下是[3,2,...], 而情况A是[2,3,...], 明显出现了重复

所以正确的代码如下

class Solution {
public:
//出参入参
    vector<vector<int>> res;
    vector<int> path;
    int sum = 0;
    void bfs(vector<int>& arr, int tar, int l){
        //终止条件
        if(sum > tar) return;
        if(sum == tar){
            res.push_back(path);
            return;
        }
        //单层逻辑
        for(int i =l ;i < arr.size();i++){
            path.push_back(arr[i]);
            sum+=arr[i];
            bfs(arr, tar,i);
            sum-=arr[i];
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        bfs(candidates,target,0);
        return res;
    }
};

组合总和 II

class Solution {
public:
    //出入参
    vector<vector<int>> res;
    vector<int> path;
    int sum = 0;
    void bfs(vector<int> & arr, int tar, int cur){
        //终止条件
        if(sum > tar){
            return;
        }
        if(sum == tar){
            res.push_back(path);
            return;
        }
        //单层逻辑
        //同树层去重,同树枝不去重
        unordered_map<int, int> map;
        for(int i = cur; i<arr.size();i++){
            map[arr[i]]++;
            if(map[arr[i]] >= 2){
                //cout<<arr[i]<<" ";
                continue;
            } 
            // cout<<arr[i]<<" ";
            path.push_back(arr[i]);
            sum+=arr[i];
            bfs(arr, tar, i+1);
            path.pop_back();
            sum-=arr[i];
        }
        
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        
        bfs(candidates,target,0);
        
        return res;
    }
};

分割回文串

class Solution {
public:
    bool isreverse(string &tar){
        int l = 0;
        int r = tar.size()-1;
        while(r>=l){
            if(tar[l] != tar[r]) return false;
            r--;
            l++;
        }
        return true;
    }
    vector<vector<string>> res;
    vector<string> path;
    void bfs(string tar, int l, int r){
        //终止条件
        if(l > r) {
            res.push_back(path);
            return;
        }
        //单层逻辑
        for(int i = l; i<= r;i++){
            string tmp = tar.substr(l, i-l+1);
            if(isreverse(tmp)){
                path.push_back(tmp);
                bfs(tar, i+1, r);
                path.pop_back();
            }
            else continue;
        }


    }
    vector<vector<string>> partition(string s) {
        bfs(s, 0, s.size()-1);
        return res;
    }
};

复原ip 地址

class Solution {
public:
    bool ipcheck(string tar){
        if(tar.size()<=0 || tar.size() > 3) return false;
        if(tar[0] == '0' && tar.size()>1) return false;
        int num = stoi(tar);
        if(num>255 || num < 0) return false;
        return true;
    }
    vector<string>res;
    string path;
    int count = 0;
    void bfs(string tar, int l, int r){
        if(count == 3){
            string tmp = tar.substr(l, r-l+1);
            if(ipcheck(tmp)){
                path+=tmp;
                res.push_back(path);
            }
            return;
        }
        if(l>r) return;
        for(int i = l; i<=r;i++){
            string tmp = tar.substr(l, i-l+1);
            if(ipcheck(tmp)){
                count++;
                string tmp_path = path;
                path+=tmp;
                path+=".";
                bfs(tar,i+1,r);
                count--;
                path = tmp_path;
            }

        }

    }
    vector<string> restoreIpAddresses(string s) {
        bfs(s,0,s.size()-1);
        return res;
    }
};

子集

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void bfs(vector<int> & nums, int l, int r){
        //终止条件
        if(l>r) return;
        //单层逻辑
        
        for(int i = l; i<=r; i++){
            path.push_back(nums[i]);
            res.push_back(path);
            bfs(nums,i+1,r);
            path.pop_back();
        }
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        res.push_back(vector<int>(0));
        bfs(nums, 0, nums.size()-1);
        return res;
    }
};

子集II

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

    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        res.push_back(vector<int>(0));
        bfs(nums, 0, nums.size()-1);
        return res;
    }
};

递增子序列

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void bfs(vector<int> & nums, int l, int r){
        //终止条件
        if(path.size() >=2) {
            res.push_back(path);
        }
        if(l>r) return;
        //单层循环
        unordered_map<int, int> map;
        for(int i = l; i<= r; i++){
            map[nums[i]]++;
            if((path.size() == 0 || path[path.size()-1] <= nums[i]) && map[nums[i]] == 1){
                path.push_back(nums[i]);
                bfs(nums,i+1, r);
                path.pop_back();
            }
        }


    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        bfs(nums, 0 , nums.size()-1);
        return res;
    }
};

全排列

class Solution {
public:
//出入参
    vector<vector<int>> res;
    vector<int> path;
    int cnt = 0;
    void bfs(vector<int> & nums, vector<bool> &used){
        //终止条件
        if(cnt == nums.size()) {
            res.push_back(path);
            return;
        }
        //单层逻辑
        for(int i =0; i< nums.size(); i++){
            if(!used[i]){
                used[i] = 1;
                cnt++;
                path.push_back(nums[i]);
                bfs(nums, used);
                used[i] = 0;
                cnt--;
                path.pop_back(); 
            }
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<bool> used(nums.size(), 0);
        bfs(nums, used);
        return res;
    }
};

全排列II

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void bfs(vector<int> &nums, vector<bool> & used){
        if(path.size() == nums.size()){
            res.push_back(path);
            return;
        }
        for(int i = 0; i<nums.size();i++){
            if(i>0 && nums[i] == nums[i-1] && used[i-1] == false) continue;
            if(!used[i]){
                used[i] = 1;
                path.push_back(nums[i]);
                bfs(nums, used);
                used[i] = 0;
                path.pop_back();
            }
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<bool> used(nums.size(), 0);
        sort(nums.begin(), nums.end());
        bfs(nums, used);
        return res;
    }
};

重新安排行程

class Solution {
public:
    unordered_map<string, map<string, int>> targets;
    vector<string> path;
    int len;
    bool dfs(string key){
        if(path.size() == len){
            return true;
        }
        for(auto &cur:targets[key]){
            if(cur.second){
                cur.second--;
                path.push_back(cur.first);
                if(dfs(cur.first)) return true;
                cur.second++;
                path.pop_back();
            }
        }
        return false;

    }
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        len = tickets.size()+1;
        for(auto &tic: tickets){
            targets[tic[0]][tic[1]]++;
        }
        path.push_back("JFK");
        dfs("JFK");
        return path;
    }
};

n皇后

class Solution {
public:
    vector<vector<string>> res;
    vector<string> path;
    bool check(int row, int col, int n){
        //检查列重复
        for(int i = 0; i <n ;i++){
            if(path[i][col] == 'Q') return 0;
        }
        //检查对角线
        int i = row-1,j = col-1;
        while(i>=0 && j>=0){
            if(path[i][j] ==  'Q') return 0;
            i--;
            j--;
        }
        i = row-1, j = col+1;
        while(j<n && i>=0){
            if(path[i][j] ==  'Q') return 0;
            i--;
            j++;
        }
        return 1;
    }
    void dfs(int n, int row){
        if(row == n){
            res.push_back(path);
            return;
        }
        for(int col = 0; col<n; col++){
            if(check(row, col, n)){
                path[row][col] = 'Q';
                dfs(n, row+1);
                path[row][col] = '.';
            }
        }


    }
    vector<vector<string>> solveNQueens(int n) {
        path = vector<string>(n, string(n,'.'));
        dfs(n, 0);
        return res;
    }
};
posted @ 2025-04-09 15:43  玉米面手雷王  阅读(8)  评论(0)    收藏  举报