当前位置: 首页 > news >正文

图解力扣回溯及剪枝问题的模板应用

文章目录

  • 选哪个的问题
    • 17. 电话号码的字母组合
      • 题目描述
      • 解题代码
      • 图解
      • 复杂度
  • 选不选的问题
    • 78. 子集
      • 题目描述
      • 解题代码
      • 图解
      • 复杂度
  • 两相转化
    • 77. 组合
      • 题目描述
      • 解题代码
        • 法一:按选哪个的思路
        • 法二:按选不选的思路
      • 图解
        • 选哪个:
        • 选不选
      • 复杂度

选哪个的问题

17. 电话号码的字母组合

题目描述

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

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

img

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

输入:digits = ""
输出:[]

示例 3:

输入:digits = "2"
输出:["a","b","c"]

提示:

  • 0 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。

解题代码

class Solution {
    string MAPPING[10]= {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
    vector<string> letterCombinations(string digits) {
        int n = digits.size();
        if(n == 0){
            return {};
        }
        vector<string> ans;
        string path(n, 0);
        auto dfs = [&](this auto&& dfs, int i){
            if(i == n){
                ans.push_back(path);
                return;
            }
            for(char c: MAPPING[digits[i] - '0']){
                path[i] = c;//元素覆盖
                dfs(i+1);
            }
        };
        dfs(0);
        return ans;

    }
};

图解

path存储每次得到的序列
核心代码:

for(char c: MAPPING[digits[i] - '0']){
	path[i] = c;
    dfs(i+1);
}

在这里插入图片描述

复杂度

时间复杂度:O(N*4^N)

注意,每次ans.push_back(path);也需要N的时间,所以前面乘N

空间复杂度:O(N*4^N)

ans 存储所有字母组合,共有 O(4^n) 个组合,递归存了n个点

选不选的问题

78. 子集

题目描述

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

解题代码

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return {};
        }
        vector<int> path;
        vector<vector<int>> ans;
        auto dfs = [&](this auto&& dfs, int i){
            if(i == n){
                ans.push_back(path);
                return;
            }
            dfs(i+1);//不选

            path.push_back(nums[i]);//选
            dfs(i+1);
            path.pop_back();//恢复现场
        };
        dfs(0);
        return ans;
    }
};

图解

在这里插入图片描述

选不选和排列问题有个区别是,如果某个数不被选择,空元素不能直接覆盖掉上一次递归中放在这个位置的元素,所以如果元素被选择,必须在递归结束后将该位置恢复到什么都没有的状态,也就是恢复现场,保证下一次递归不被影响。

复杂度

时间复杂度:O(N2^N)
空间复杂度:O(N
2^N)

两相转化

77. 组合

题目描述

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

示例 2:

输入:n = 1, k = 1
输出:[[1]]

提示:

  • 1 <= n <= 20
  • 1 <= k <= n

解题代码

法一:按选哪个的思路
class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        vector<int> path;
        vector<vector<int>> ans;
        auto dfs = [&](this auto&& dfs, int i){
            int d = k - path.size();
            if(d == 0){ //取path长度合适的数组加入ans
                ans.push_back(path);
                return;
            }

            for(int j=i; j>=d; j--){
                path.push_back(j);
                dfs(j-1);
                path.pop_back();
            }
        };
        dfs(n);
        return ans;
    }
};
法二:按选不选的思路
class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        vector<int> path;
        vector<vector<int>> ans;
        auto dfs = [&](this auto&& dfs, int i){
            int d = k - path.size();
            if(d == 0){ //取path长度合适的数组加入ans
                ans.push_back(path);
                return;
            }

            if(i < d){
                return;
            }
            dfs(i-1);
            
            path.push_back(i);
            dfs(i-1);
            path.pop_back();
        };
        dfs(n);
        return ans;
    }
};

图解

选哪个:

在这里插入图片描述
这是剪枝后的情况,下面画一个正序、不引入d的没有剪枝的情况:
在这里插入图片描述
显然会冗余很多,代码也贴在这供大家参考:

class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        vector<int> path;
        vector<vector<int>> ans;
        auto dfs = [&](this auto&& dfs, int i){
        
            if(path.size() == k){ //取path长度合适的数组加入ans
                ans.push_back(path);
                return;
            }

            for(int j=i; j<=n; j++){
                path.push_back(j);
                dfs(j+1);
                path.pop_back();
            }
        };
        dfs(1);
        return ans;
    }
};
选不选

这种方法注意一点:只要不选,d就不会变,也就意味着两个节点如果是兄弟节点(父节点一样),那么他们的d相等,而一旦i < d,意味着这条线不可能出答案了,因为数字不够用,这个不只是剪枝,而且是必要的,不写会越界
在这里插入图片描述

复杂度

两个都是
时间复杂度:O(C(N,K)) (是排列组合的那个符号)
空间复杂度:O(N)


……可算画完了,好悬没给我累死

相关文章:

  • ctfshow VIP题目限免 密码逻辑脆弱
  • 区间 dp 系列 题解
  • 《深入探秘:分布式软总线自发现、自组网技术原理》
  • 部署大模型不再难:DeepSeek + 腾讯云 HAI 实战教程
  • Java 列表初始化全解析:7种方式详解与最佳实践
  • SpringBoot和微服务学习记录Day2
  • python基础语法10-异常处理
  • TPS入门DAY03 服务器篇
  • 提示词工程
  • 牛客KY257 日期累加
  • 逆向工程的多层次解析:从实现到领域的全面视角
  • Spark核心知识总结
  • 2025年汽车加气站操作工证考试内容
  • html元素转图像之深入探索 html - to - image:功能、应用与实践
  • 探索原生JS的力量:自定义实现类似于React的useState功能
  • popupwindow拦截返回点击
  • 学习笔记083——Java Stream API
  • 第七天 开始Unity Shader的学习之Unity中的基础光照之高光反射光照模型
  • S7-1200 PLC热电偶和热电阻模拟量模块
  • java基础-修饰符
  • 厦门网站开发平台/seo的重要性
  • 新疆5g基站建设/2023年8月新冠疫情
  • 通过社交网站来做招聘决定/html简单网页成品
  • 婚庆公司网站制作/自己开网店怎么运营
  • 山东东营建设网官方网站/seo技术培训广东
  • 广州天河区做网站的公司/在线seo优化