leetcode算法刷题的第十九天

今天是回溯算法。

1.回溯算法理论基础

回溯法也可以叫做回溯搜索法,它是一种搜索的方式。

回溯函数也就是递归函数,指的都是一个函数。

回溯的本质就是穷举,穷举所有的可能,然后选出我们想要的答案,如果想要让回溯高效一些,可以加一些剪枝的操作,但也改变不了回溯法就是穷举的本质。

因为没得选,一些问题能暴力搜出来就不错了,撑死了再剪枝一下,还没有更高效的解法。

回溯法解决的问题

组合问题:N个数里面按一定规则找出k个数的集合

切割问题:一个字符串按一定规则有几种切割方式

子集问题:一个N个数的集合里有多少符合条件的子集

排列问题:N个数按一定规则全排列,有几种排列方式

棋盘问题:N皇后,解数独等等

组合是不强调元素顺序的,排列是强调元素顺序

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

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

回溯和递归是相辅相成的。

2.leetcode 77.组合

题目链接

class Solution {
public:
    vector<vector<int>> result;// 存放符合条件结果的集合
    vector<int> path;// 用来存放符合条件结果
    void backtracking(int n,int k,int startIndex){
        if(path.size()==k){
            result.push_back(path);
            return;
        }
        for(int i=startIndex;i<=n;i++){
            path.push_back(i);// 处理节点
            backtracking(n,k,i+1);//递归,从下一个数字开始递归
            path.pop_back();// 回溯,撤销处理的节点
        }
    }
    vector<vector<int>> combine(int n, int k) {
        backtracking(n,k,1);
        return result;
    }
};

思路总结:这道题的题解可以看做回溯算法题目的模板,下次写的时候就可以看一下,这样就会有一个大致的思路。为什么要pop_back?因为如果不加这个的话,加了1之后再加上2,这样会一直把数字加进去,这样就没有13这个组合了,更没有后面的组合了。

3.leetcode 216.组合总和III

题目链接

class Solution {
public:
    vector<vector<int>> result;// 存放结果集
    vector<int> path;// 符合条件的结果
    // targetSum:目标和,也就是题目中的n。
    // k:题目中要求k个数的集合。
    // sum:已经收集的元素的总和,也就是path里元素的总和。
    // startIndex:下一层for循环搜索的起始位置。
    void backtracking(int targetSum,int k,int sum,int startIndex){
        if(path.size()==k){
            if(sum==targetSum) result.push_back(path);
            return;// 如果path.size() == k 但sum != targetSum 直接返回
        }
        for(int i=startIndex;i<=9;i++){
            sum+=i;// 处理
            path.push_back(i);// 处理
            backtracking(targetSum,k,sum,i+1);// 注意i+1调整startIndex
            sum-=i;// 回溯
            path.pop_back();// 回溯
        }
    }
    vector<vector<int>> combinationSum3(int k, int n) {
        backtracking(n,k,0,1);
        return result;
    }
};

思路总结:相对来说加了元素总和的限制,这道题依然是把问题抽象为树形结构,然后按照回溯模板进行解决。

4.leetcode 17.电话号码的字母组合

题目链接

class Solution {
public:
    const string letterMap[10]={
        "",//0
        "",//1
        "abc",//2
        "def",//3
        "ghi",//4
        "jkl",//5
        "mno",//6
        "pqrs",//7
        "tuv",//8
        "wxyz",//9
    };
    vector<string> result;
    string s;
    void backtracking(const string& digits,int index){
        if(index==digits.size()){
            result.push_back(s);
            return;
        }
        int digit=digits[index]-'0';// 将index指向的数字转为int
        string letters=letterMap[digit];// 取数字对应的字符集
        for(int i=0;i<letters.size();i++){
            s.push_back(letters[i]);// 处理
            backtracking(digits,index+1);// 递归,注意index+1,一下层要处理下一个数字了
            s.pop_back(); // 回溯
        }
    }
    vector<string> letterCombinations(string digits) {
        if(digits.size()==0){
            return result;
        }
        backtracking(digits,0);
        return result;
    }
};

注意:输入1 * #按键等等异常情况

代码中最好考虑这些异常情况,但题目的测试数据中应该没有异常情况的数据,所以我就没有加了。

但是要知道会有这些异常,如果是现场面试中,一定要考虑到!

思路总结:

本题是多个集合求组合,所以在回溯的搜索过程中,都有一些细节需要注意的。

其实本题不算难,但也处处是细节,大家还要自己亲自动手写一写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值