代码随想录算法训练营day22 | 77. 组合、216.组合总和III 、17.电话号码的字母组合

news2024/11/24 20:38:56

碎碎念:加油
参考:代码随想录

回溯算法理论基础

回溯和递归是相辅相成的,只要有递归,就会有回溯。回溯通常在递归函数的下面。
回溯搜索到法的效率: 它其实是纯暴力的做法,不是一个高效的算法。
回溯法能解决的问题: 组合问题(不强调元素的顺序)、切割问题、子集问题、排列问题(强调元素的顺序)、棋盘问题(N皇后)
如何理解回溯法: 所有回溯法都可以抽象为一个树行结构。一般来说树的宽度是回溯法中处理的集合的大小,树的深度就是递归的深度。
回溯法的模板:

void backtracking(参数){
    if (终止条件) {
        收集结果;
        return;
    }
    for (遍历集合的每一个元素){
        处理节点;
        递归函数;
        回溯操作; // 撤销处理节点
    }
    return;
}

77. 组合

题目链接

77. 组合

思想

看到这题我们自然而然想到暴力做法,如果k是2,那么就用两层for循环,但是k稍微大点,这么做就显然做不出来了,时间复杂度太大了。
回溯法是通过递归来控制有多少层for的。
上面提到过,回溯法做题可以抽象出一个树形图,本题抽象出来的树形图如图。
在这里插入图片描述

叶子节点就是我们要求的所有组合。通过维护一个参数startindex来控制搜索的起始位置。
定义一个一维数组path,定义一个二维数组result
回溯三部曲:

  1. 递归函数的参数和返回值:一般返回值都是void,参数n,k,startindex
  2. 终止条件:path的大小等于k,就要收割结果。
  3. 单层搜索的逻辑:从startindex开始遍历后面的元素,用path收集路径上的元素,递归下一层,回溯(把之前收集到的一个元素pop出去)。

剪枝:
在这里插入图片描述
优化上面单层搜索的逻辑:
其实图里每个节点都是for循环。那么我们要做的优化就在于for循环里i的范围。
path存放着已经选取到的元素,还剩k-path.size()需要选取,这些元素至多要从n-(k-path.size())+1的位置开始。
[x, n]的数组长度起码应该是k-path.size(),这才有继续搜索的必要, 有 n-x+1 = k-path.size() ,得 x = n+1 - (k-path.size()), 而且这个x是可以作为起点往下搜的,也就是for(i = s; i<=x; i++) 这里的x是可以取到的。
体现在代码里,修改为i <= n-(k-path.size())+1即可。

题解

// cpp
class Solution {
private:
    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();
        }
    }
public:
    vector<vector<int>> combine(int n, int k) {
        backtracking(n, k, 1);
        return result;
    }
};

# python 剪枝优化
class Solution:
    def __init__(self):
        self.result = []
        self.path = []
    
    def backtracking(self, n, k, startIndex):
        if len(self.path) == k:
            self.result.append(self.path[:])
            return
        
        for i in range(startIndex, n - (k - len(self.path)) + 2):
            self.path.append(i)
            self.backtracking(n, k, i + 1)
            self.path.pop()

    def combine(self, n: int, k: int) -> List[List[int]]:
        self.backtracking(n, k, 1)
        return self.result

反思

注意组合是无序的,组合中的元素只能使用一次。

216.组合总和III

题目链接

216.组合总和III

思想

和上一题的区别在于多了一个关于和的限制。
注意本题不强调元素的顺序,比如126和216是同一个组合。
树形结构:
在这里插入图片描述
定义一个一维数组path,定义一个二维数组result
回溯三部曲:

  1. 参数和返回值:返回值为void,参数targetSum,k,sum,startIndex。
  2. 终止条件:path的size==k,判断sum和targetSum是否相等,相等就把path加入result,否则直接返回。
  3. 单层递归逻辑:从startIndex开始遍历,取数加入path,计算sum,下一层递归(startIndex传入i+1),pop出刚加入的数,修改sum。

剪枝优化:
如果sum>targetSum,直接返回;
在for循环那里也可以做剪枝,体现在代码里,修改为i <= 9-(k-path.size())+1即可。

题解

// cpp
class Solution {
private:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(int targetSum, int k, int sum, int startIndex) {
        if (path.size() == k) {
            if (sum == targetSum) result.push_back(path);
            return;
        }
        for (int i = startIndex; i <= 9; i ++) {
            sum += i;
            path.push_back(i);
            backtracking(targetSum, k, sum, i + 1);
            sum -= i;
            path.pop_back();
        }
    }
public:
    vector<vector<int>> combinationSum3(int k, int n) {
        result.clear();
        path.clear();
        backtracking(n, k, 0, 1);
        return result;
    }
};


# python 剪枝优化
class Solution:
    def __init__(self):
        self.path = []
        self.result = []
    
    def backtracking(self, targetSum, k, currentSum, startIndex):
        if currentSum > targetSum: # 剪枝
            return
        if len(self.path) == k:
            if currentSum == targetSum:
                self.result.append(self.path[:])
            return
        for i in range(startIndex, 9 - (k - len(self.path)) + 2):
            currentSum += i
            self.path.append(i)
            self.backtracking(targetSum, k,currentSum, i + 1)
            currentSum -= i
            self.path.pop()
        
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        self.backtracking(n, k, 0, 1)
        return self.result

反思

注意组合是无序的,组合中的元素只能使用一次。

17.电话号码的字母组合

题目链接

17.电话号码的字母组合

思想

树形图如图:
在这里插入图片描述

回溯三部曲:

  1. 参数和返回值:返回值是void,参数是digits,index(当前遍历到哪个数字了)
  2. 终止条件:index==digits.size() 收获结果
  3. 单层递归逻辑:用index取数字,用数字找字符串,接下来遍历得到的字符串,把字母放入s,调用递归函数(index+1),再把s取出来(回溯)。

回溯过程也可以隐藏在参数里。

题解

class Solution {
private:
    const string letterMap[10] = {
        "", // 0
        "", // 1
        "abc", // 2
        "def", // 3
        "ghi", // 4
        "jkl", // 5
        "mno", // 6
        "pqrs", // 7
        "tuv", // 8
        "wxyz", // 9
    };
public:
    vector<string> result;
    string s;
    void backtracking(const string& digits, int index) {
        if (index == digits.size()) {
            result.push_back(s);
            return;
        }
        string letters = letterMap[digits[index] - '0'];
        for (int i = 0; i < letters.size(); i ++) {
            s.push_back(letters[i]);
            backtracking(digits, index + 1);
            s.pop_back();
        }
    }
    vector<string> letterCombinations(string digits) {
        if (digits.size() == 0) return result;
        backtracking(digits, 0);
        return result;
    }
};
class Solution:
    def __init__(self):
        self.letterMap = [
            "",     # 0
            "",     # 1
            "abc",  # 2
            "def",  # 3
            "ghi",  # 4
            "jkl",  # 5
            "mno",  # 6
            "pqrs", # 7
            "tuv",  # 8
            "wxyz"  # 9
        ]
        self.result = []
        self.s = ""
    
    def backtracking(self, digits, index):
        if index == len(digits):
            self.result.append(self.s)
            return
        letters = self.letterMap[int(digits[index])]
        for i in range(len(letters)):
            self.s += letters[i]
            self.backtracking(digits, index + 1)
            self.s = self.s[:-1]

    
    def letterCombinations(self, digits: str) -> List[str]:
        if len(digits) == 0:
            return self.result
        self.backtracking(digits, 0)
        return self.result
        

反思

前两题都是在一个集合里来求组合,所以需要用到startIndex,本题是在不同的集合里,不需要用参数来控制之前遍历的元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1953047.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将控制台内容输出到文本文件

示例代码&#xff1a; Imports System.IO Module Module1Sub Main()Dim fs As New FileStream("D:\Desktop\test\输出结果.txt", FileMode.Create, FileAccess.Write, FileShare.None)Dim sw As New StreamWriter(fs)Console.SetOut(sw)Console.SetError(sw)For i …

Spring 自定义集合实现策略

Spring 自定义集合实现策略 日常开发中&#xff0c;如果遇到复杂业务通常会用一个接口实现多个实现类。需要根据对应参数判断获取不同实现类。例如支付场景&#xff0c;根据选择支付方式&#xff0c;选择相应路由。如果实现类不多&#xff0c;通常会这样。如果实现类多了&…

【论文10】复现代码tips

一、准备工作 1.创建一个虚拟环境 conda create --name drgcnn38 python=3.8.18 2.激活虚拟环境 conda activate drgcnn38 注意事项 在Pycharm中终端(terminal)显示PS而不是虚拟环境base 问题如下所示 解决方法:shell路径改成cmd.exe 重启终端显示虚拟环境 3.安装torch …

Linux:Linux进程控制

目录 1. 进程概念 1.1 并行和并发 2. 进程创建 2.1 fork()函数初识 2.2 写时拷贝 2.3 fork常规用法 2.4 fork调用失败的原因 3. 进程终止 3.1 进程场景 3.2 进程常见退出方法 4. 进程等待 4.1 进程等待必要性 4.2 进程等待的方法 4.2.1 wait方法&#xff1a; 4.…

推荐系统三十六式学习笔记:工程篇.常见架构24|典型的信息流架构是什么样的

目录 整体框架数据模型1.内容即Activity2.关系即连接 动态发布信息流排序数据管道总结 从今天起&#xff0c;我们不再单独介绍推荐算法的原理&#xff0c;而是开始进入一个新的模块-工程篇。 在工程实践的部分中&#xff0c;我首先介绍的内容是当今最热门的信息流架构。 信息…

关于数据存储位置的一点知识

关于数据存储位置的一点知识

c++红黑树,插入公式

概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路 径会比其他路径长出俩倍&#xff0c;因而是接近…

useRoute 函数的详细介绍与使用示例

title: useRoute 函数的详细介绍与使用示例 date: 2024/7/27 updated: 2024/7/27 author: cmdragon excerpt: 摘要&#xff1a;本文介绍了Nuxt.js中useRoute函数的详细用途与示例&#xff0c;展示了如何在组合式API中使用useRoute获取当前路由信息&#xff0c;包括动态参数、…

web服务器dns服务器配置服务

1.搭建一个nfs服务器&#xff0c;客户端可以从该服务器的/share目录上传并下载文件 server服务器&#xff1a; 创建 /share目录&#xff0c;并且编辑/etc/exports文件 更改目录权限为755&#xff1a; 755权限码的含义是&#xff1a; 文件所有者&#xff08;第一位数字7&…

Docker Desktop安装(通俗易懂)

1、官网 https://www.docker.com/products/docker-desktop/ 2、阿里云镜像 docker-toolbox-windows-docker-for-windows安装包下载_开源镜像站-阿里云 1. 双击安装文件勾选选项 意思就是&#xff1a; Use WSL 2 instead of Hyper-V (recommended) : 启用虚拟化&#xff0c;…

20240727生活沉思------------关于报考软考高级架构师

软考高级架构师 软考高级架构师 缴费 主要是报的千峰 1880元。 相对来说还算可以吧。。。其他也没给我机会选择啊 备考 我现在开始备考&#xff0c;考试时间2024年11月。 今天是正式开始7.27号。 给大家看看接下来我的课程安排&#xff1a; 额&#xff0c;还是满满当当的…

Redis从入门到超神-(十二)Redis监听Key的过期事件

前言 试想一个业务场景&#xff0c;订单超过30分钟未支付需要做自动关单处理,修改订单状态&#xff0c;库存回退等&#xff0c;你怎么实现&#xff1f;方案一&#xff1a;可以使用定时任务扫表&#xff0c;通过支付状态和下单时间来判断是否支付过期。但是这样的方案是非常消耗…

Vue入门(二)常用指令

一、Vue 常用指令 Vue 常用指令 - 指令&#xff1a;是带有 v- 前缀的特殊属性&#xff0c;不同指令具有不同含义。例如 v-html&#xff0c;v-if&#xff0c;v-for。 - 使用指令时&#xff0c;通常编写在标签的属性上&#xff0c;值可以使用 JS 的表达式。 - 常用指令 二、常用…

邦布带你从零开始实现图书管理系统(java版)

今天我们来从零开始实现图书管理系统。 图书管理系统 来看我们的具体的实现&#xff0c;上述视频。 我们首先来实现框架&#xff0c;我们要实现图书管理系统&#xff0c;首先要搭框架。 我们首先定义一个书包&#xff0c;在书包中定义一个书类和一个书架类&#xff0c;再定义…

AI大模型写的高考作文,你觉得怎么样?

下方为2024年高考新课标I卷语文作文题目&#xff0c;接下来将使用Chatgpt、文心一言、讯飞星火等众多AI大模型来写这篇高考作文 Chatgpt 标题&#xff1a;问题的演变与思考 随着互联网和人工智能技术的飞速发展&#xff0c;我们获得信息的方式和速度发生了前所未有的变化。曾经…

用PyTorch从零开始编写DeepSeek-V2

DeepSeek-V2是一个强大的开源混合专家&#xff08;MoE&#xff09;语言模型&#xff0c;通过创新的Transformer架构实现了经济高效的训练和推理。该模型总共拥有2360亿参数&#xff0c;其中每个令牌激活21亿参数&#xff0c;支持最大128K令牌的上下文长度。 在开源模型中&…

C嘎嘎浅谈模板

这篇文章给大家介绍一下c嘎嘎内存管理和模板&#xff0c;那么我们直接进入正题 c/c的程序内存分布 这里的了解一下即可 new和delete的定义和操作 格式&#xff1a;类型* 对象名 new 类型&#xff1b; 数组(对象)定义格式&#xff1a;类型* 对象名 new 类型[元素个数]&…

【安卓开发】【Android】如何进行真机调试【注意事项】

一、所需原料 1、电脑&#xff08;安装有Android Studio开发工具&#xff09;&#xff1a; 2、安卓操作系统手机&#xff1a;笔者演示所用机型为huawei rongyao50&#xff0c;型号为NTU-AN00&#xff1a; 3、数据线&#xff08;usb-typeA&#xff09;一根。 二、操作步骤 1…

支持向量机回归及其应用(附Python 案例代码)

使用支持向量机回归估计房价 让我们看看如何使用支持向量机&#xff08;SVM&#xff09;的概念构建一个回归器来估计房价。我们将使用sklearn中提供的数据集&#xff0c;其中每个数据点由13个属性定义。我们的目标是根据这些属性估计房价。 引言 支持向量回归&#xff08;SV…

IndexError: index 0 is out of bounds for axis 1 with size 0

IndexError: index 0 is out of bounds for axis 1 with size 0 目录 IndexError: index 0 is out of bounds for axis 1 with size 0 【常见模块错误】 【解决方案】 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#…