代码随想录训练营 Day23打卡 回溯算法part02 39. 组合总和 40. 组合总和II 131. 分割回文串

news2024/12/23 14:23:59

代码随想录训练营 Day23打卡 回溯算法part02

一、 力扣39. 组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 :
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

本题搜索的过程抽象成树形结构如下:
在这里插入图片描述
注意图中叶子节点的返回条件,因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!

版本一 回溯

class Solution:

    def backtracking(self, candidates, target, total, startIndex, path, result):
        if total > target:  # 剪枝操作,如果当前和超过目标值,提前返回
            return
        if total == target:  # 如果当前和等于目标值,找到一个有效组合
            result.append(path[:])  # 将当前路径加入结果集
            return

        for i in range(startIndex, len(candidates)):
            total += candidates[i]  # 将当前数字加入当前和
            path.append(candidates[i])  # 将当前数字加入路径
            self.backtracking(candidates, target, total, i, path, result)  # 递归调用,注意起始索引不变
            total -= candidates[i]  # 回溯,撤销处理的节点
            path.pop()  # 回溯,从路径中移除最后一个元素

    def combinationSum(self, candidates, target):
        result = []
        self.backtracking(candidates, target, 0, 0, [], result)
        return result

版本二 回溯(带剪枝)

class Solution:

    def backtracking(self, candidates, target, total, startIndex, path, result):
        if total == target:  # 如果当前和等于目标值,找到一个有效组合
            result.append(path[:])  # 将当前路径加入结果集
            return

        for i in range(startIndex, len(candidates)):
            if total + candidates[i] > target:  # 剪枝操作,如果当前和加上候选数字超过目标值,提前结束循环
                break
            total += candidates[i]  # 将当前数字加入当前和
            path.append(candidates[i])  # 将当前数字加入路径
            self.backtracking(candidates, target, total, i, path, result)  # 递归调用,注意起始索引不变
            total -= candidates[i]  # 回溯,撤销处理的节点
            path.pop()  # 回溯,从路径中移除最后一个元素

    def combinationSum(self, candidates, target):
        result = []
        candidates.sort()  # 对候选数字进行排序,以便于剪枝操作
        self.backtracking(candidates, target, 0, 0, [], result)
        return result

力扣题目链接
题目文章讲解
题目视频讲解

二、 力扣40. 组合总和II

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次
注意:解集不能包含重复的组合。
示例 :
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

这道题目和39.组合总和 (opens new window)如下区别:

  1. 本题candidates 中的每个数字在每个组合中只能使用一次。
  2. 本题数组candidates的元素是有重复的,而39.组合总和 (opens new
    window)是无重复元素的数组candidates

最后本题和39.组合总和 (opens new window)要求一样,解集不能包含重复的组合。

本题的难点在于区别2中:集合(数组candidates)有重复元素,但还不能有重复的组合。

选择过程树形结构如图所示:
在这里插入图片描述

版本一 回溯

class Solution:
    def backtracking(self, candidates, target, total, startIndex, used, path, result):
        if total == target:  # 如果当前和等于目标值,找到一个有效组合
            result.append(path[:])  # 将当前路径加入结果集
            return

        for i in range(startIndex, len(candidates)):
            # 对于相同的数字,只选择第一个未被使用的数字,跳过其他相同数字
            if i > startIndex and candidates[i] == candidates[i - 1] and not used[i - 1]:
                continue

            if total + candidates[i] > target:  # 剪枝操作,如果当前和加上候选数字超过目标值,提前结束循环
                break

            total += candidates[i]  # 将当前数字加入当前和
            path.append(candidates[i])  # 将当前数字加入路径
            used[i] = True  # 标记当前数字为已使用
            self.backtracking(candidates, target, total, i + 1, used, path, result)  # 递归调用
            used[i] = False  # 回溯,撤销处理的节点
            total -= candidates[i]  # 回溯,从当前和中减去该数字
            path.pop()  # 回溯,从路径中移除最后一个元素

    def combinationSum2(self, candidates, target):
        used = [False] * len(candidates)  # 初始化用于跟踪使用情况的布尔数组
        result = []
        candidates.sort()  # 对候选数字进行排序
        self.backtracking(candidates, target, 0, 0, used, [], result)
        return result

版本二 回溯(优化)

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        candidates.sort()  # 对候选数字进行排序
        results = []
        self.combinationSumHelper(candidates, target, 0, [], results)
        return results

    def combinationSumHelper(self, candidates, target, index, path, results):
        if target == 0:  # 如果目标值为零,找到一个有效组合
            results.append(path[:])  # 将当前路径加入结果集
            return
        for i in range(index, len(candidates)):
            if i > index and candidates[i] == candidates[i - 1]:  # 跳过相同数字的重复使用
                continue  
            if candidates[i] > target:  # 剪枝操作,如果当前候选数字超过目标值,提前结束循环
                break  
            path.append(candidates[i])  # 将当前数字加入路径
            self.combinationSumHelper(candidates, target - candidates[i], i + 1, path, results)  # 递归调用
            path.pop()  # 回溯,从路径中移除最后一个元素

力扣题目链接
题目文章讲解
题目视频讲解

三、 力扣131. 分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
示例
输入:s = “aab”
输出:[[“a”,“a”,“b”],[“aa”,“b”]]

切割问题,也可以抽象为一棵树形结构,如图:
在这里插入图片描述

版本一 回溯

  1. 初始化和启动回溯:

    在 partition 方法中,初始化一个空的结果集 result。
    调用 backtracking 方法开始回溯过程。

  2. 回溯函数 backtracking:

    如果当前起始索引 start_index 等于字符串长度,表示找到一种分割方法,将当前路径加入结果集。
    否则,从当前起始索引开始遍历字符串的每个子串:
        检查子串是否为回文。
        如果是回文,将子串加入路径,递归处理剩余字符串。
        回溯时移除最后加入的子串,恢复状态。

  3. 判断回文函数 is_palindrome:

    逐字符比较子串的两端,判断其是否为回文。

from typing import List

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        '''
        递归用于纵向遍历
        for循环用于横向遍历
        当切割线迭代至字符串末尾,说明找到一种方法
        类似组合问题,为了不重复切割同一位置,需要start_index来做标记下一轮递归的起始位置(切割线)
        '''
        result = []
        self.backtracking(s, 0, [], result)
        return result

    def backtracking(self, s, start_index, path, result ):
        # Base Case
        if start_index == len(s):
            result.append(path[:])
            return
        
        # 单层递归逻辑
        for i in range(start_index, len(s)):
            # 判断被截取的这一段子串([start_index, i])是否为回文串
            if self.is_palindrome(s, start_index, i):
                path.append(s[start_index:i+1])
                self.backtracking(s, i+1, path, result)  # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
                path.pop()  # 回溯

    def is_palindrome(self, s: str, start: int, end: int) -> bool:
        i = start        
        j = end
        while i < j:
            if s[i] != s[j]:
                return False
            i += 1
            j -= 1
        return True 

版本二 回溯(优化)

  1. 初始化和启动回溯:

    在 partition 方法中,初始化一个空的结果集 result。
    调用 backtracking 方法开始回溯过程。

  2. 回溯函数 backtracking:

    如果当前起始索引 start_index 等于字符串长度,表示找到一种分割方法,将当前路径加入结果集。
    否则,从当前起始索引开始遍历字符串的每个子串:
        检查子串是否为回文。
        如果是回文,将子串加入路径,递归处理剩余字符串。
        回溯时移除最后加入的子串,恢复状态。

  3. 判断回文:

    直接通过切片和反转字符串来判断子串是否为回文。

from typing import List

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        result = []
        self.backtracking(s, 0, [], result)
        return result

    def backtracking(self, s, start_index, path, result ):
        if start_index == len(s):
            result.append(path[:])
            return
        
        for i in range(start_index, len(s)):
            # 若反序和正序相同,意味着这是回文串
            if s[start_index: i + 1] == s[start_index: i + 1][::-1]:
                path.append(s[start_index:i+1])
                self.backtracking(s, i+1, path, result)  # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
                path.pop()  # 回溯

版本三 高效判断回文子串

  1. 初始化和启动回溯:

    在 partition 方法中,初始化一个布尔矩阵 isPalindrome,用于记录子串是否为回文。
    调用 computePalindrome 预处理所有子串,填充 isPalindrome 矩阵。
    调用 backtracking 方法开始回溯过程。

  2. 回溯函数 backtracking:

    如果当前起始索引 startIndex 等于字符串长度,表示找到一种分割方法,将当前路径加入结果集。
    否则,从当前起始索引开始遍历字符串的每个子串:
        检查子串是否为回文(通过 isPalindrome 矩阵)。
        如果是回文,将子串加入路径,递归处理剩余字符串。
        回溯时移除最后加入的子串,恢复状态。

  3. 预处理回文子串:

    使用动态规划填充 isPalindrome 矩阵,记录每个子串是否为回文。

from typing import List

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        result = []
        isPalindrome = [[False] * len(s) for _ in range(len(s))]  # 初始化isPalindrome矩阵
        self.computePalindrome(s, isPalindrome)
        self.backtracking(s, 0, [], result, isPalindrome)
        return result

    def backtracking(self, s, startIndex, path, result, isPalindrome):
        if startIndex >= len(s):
            result.append(path[:])
            return

        for i in range(startIndex, len(s)):
            if isPalindrome[startIndex][i]:  # 是回文子串
                substring = s[startIndex:i + 1]
                path.append(substring)
                self.backtracking(s, i + 1, path, result, isPalindrome)  # 寻找i+1为起始位置的子串
                path.pop()  # 回溯过程,弹出本次已经添加的子串

    def computePalindrome(self, s, isPalindrome):
        for i in range(len(s) - 1, -1, -1):  # 需要倒序计算,保证在i行时,i+1行已经计算好了
            for j in range(i, len(s)):
                if j == i:
                    isPalindrome[i][j] = True
                elif j - i == 1:
                    isPalindrome[i][j] = (s[i] == s[j])
                else:
                    isPalindrome[i][j] = (s[i] == s[j] and isPalindrome[i+1][j-1])

力扣题目链接
题目文章讲解
题目视频讲解

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

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

相关文章

李飞飞亲自撰文,数十名科学家签署联名信,反对加州AI限制法案

AI真的已经危险到要如此监管的地步了吗&#xff1f; 点击访问我的技术博客https://ai.weoknow.comhttps://ai.weoknow.com 在创新的热土硅谷&#xff0c;李飞飞、吴恩达等 AI 科学家正在与监管部门展开一场关于安全与创新的拉锯战。 这场拉锯战的核心是一个名叫 SB-1047 的法案…

云平台部署 FunAudioLLM 语音天花板

FunAudioLLM FunAudioLLM 是阿里开源的语音处理模型&#xff0c;包含 SenseVoice 和 CosyVoice 两个模型。可以实现 5 种语言生成&#xff0c;以及 50 种语言无缝翻译&#xff0c;还能识别语音情绪。 FunAudioLLM&#xff1a;https://github.com/FunAudioLLM CosyVoice开源仓…

【Material-UI】按钮组:垂直按钮组详解

文章目录 一、按钮组概述1. 组件介绍2. 基本用法 二、垂直按钮组的应用场景1. 导航菜单2. 表单操作3. 选项切换 三、按钮组的样式定制1. 变体&#xff08;Variants&#xff09;2. 颜色&#xff08;Colors&#xff09; 四、垂直按钮组的优势1. 空间利用2. 可读性与易用性3. 视觉…

【网络基础一】几乎不讲任何网络协议细节,搭建网络基本结构

文章目录 问题认识“协议”计算机通信问题技术问题应用问题 协议分层 统编程帮助我们处理数据&#xff0c;网络编程帮助我们获取数据&#xff0c;网络配上我们写的线程池模块很快就搭建起来了。 问题 网卡是文件吗&#xff1f; 是的&#xff0c;所以未来网络通信的本质反馈到编…

Obsidian插件安装与开发

大概背景 事情的起因还是因为做笔记&#xff0c;我喜欢利用插件Obsidian Git自动同步笔记到Gitee&#xff0c;写md文档有个问题就是关于图片如何存储。 我个人习惯是将所有图片都保存到指定的文件夹下&#xff0c;如图&#x1f447; 由于Obsidian对粘贴图片默认格式为这样的&…

ESXI加入VMware现有集群提示常规性错误

集群内有vSphere6.5和6.7的版本&#xff0c;都开启了EVC 这台老服务器是DELL R710添加时报错&#xff0c;网上查了些资料说要重装ESXI或者关闭EVC等等 最终解决方法是&#xff0c;给这台ESXI配置一个NTP服务器&#xff0c;同步系统时间&#xff0c;之后即可正常加入集群 往期文…

【安卓】文件存储

文章目录 将数据存储到文件中从文件中读取数据 文件存储是Android中最基本的数据存储方式&#xff0c;它不对存储的内容进行任何格式化处理&#xff0c;所有数据都是原封不动地保存到文件当中的&#xff0c;因而它比较适合存储一些简单的文本数据或二进制数据。如果你想使用文件…

家庭教育—情绪教育:塑造孩子情绪智力的金钥匙

文章目录 1. 背景介绍2. “1310镇静”方法的介绍3. 方法的科学依据4. 实施步骤5. 总结 1. 背景介绍 在快节奏的现代生活中&#xff0c;儿童面临着越来越多的情绪挑战。情绪教育作为素质教育的重要组成部分&#xff0c;越来越受到家长和教育者的重视。情绪教育不仅关乎儿童的心…

第100+20步 ChatGPT学习:R实现Lasso回归

基于R 4.2.2版本演示 一、写在前面 花了好几期分享了使用R语言实现机器学习分类&#xff0c;基本把常见模型都讲完了。 最后就以Lasso回归收尾得了。 Lasso回归应该很出名了&#xff0c;做特征变量筛选的&#xff0c;因此&#xff0c;不过多介绍。 二、R代码实现Lasso回归 …

OceanBase V4.2特性解析:MySQL模式下GIS空间表达式的场景及能力解析

1. 背景 1.1. OceanBase Mysql gis空间表达式的应用场景及能力 在OceanBase 4.1版本中&#xff0c;mysql模式下支持了gis数据类型以及部分空间对象相关的表达式&#xff0c;随着客户使用空间数据的需求日益增长&#xff0c;需要快速地补齐空间数据存储和计算分析的能力&#…

简单分享下Python进程

1. 单进程与多进程 理论讲解&#xff1a; 进程是操作系统中资源分配的基本单位&#xff0c;每个进程都有独立的内存空间。 多进程允许同时运行多个进程&#xff0c;提高CPU利用率和程序响应速度。 示例代码&#xff1a; import os print("当前进程ID:", os.getp…

【适配器模式】设计模式: 穿越接口的时空隧道(架起接口间的桥梁)

文章目录 Java 设计模式之适配器模式&#xff1a;理论与实践1. 引言1.1 结构型模式介绍1.2 为什么需要适配器模式&#xff1f; 2. 适配器模式概述2.1 定义2.2 关键概念2.3 适配器模式的类型 3. 适配器模式的参与者4. 适配器模式的工作原理4.1 类适配器模式的工作流程4.2 对象适…

CSS基础 - CSS3

目录 A. 简介 B. 基础用法 C. 总结 A. 简介 CSS3 是 CSS&#xff08;层叠样式表&#xff09;技术的升级版本。 一、新特性概述 选择器增强 CSS3 引入了更多强大的选择器&#xff0c;使得开发者能够更精确地选择和样式化网页元素。例如&#xff0c;属性选择器可以根据元素…

Golang | Leetcode Golang题解之第329题矩阵中的最长递增路径

题目&#xff1a; 题解&#xff1a; var (dirs [][]int{[]int{-1, 0}, []int{1, 0}, []int{0, -1}, []int{0, 1}}rows, columns int )func longestIncreasingPath(matrix [][]int) int {if len(matrix) 0 || len(matrix[0]) 0 {return 0}rows, columns len(matrix), len(m…

仓颉编程入门 -- 循环语句详解

仓颉编程入门 – 循环语句 一 . while 表达式 while 表达式的基本形式为&#xff1a; while (条件) {循环体 }注意事项 : 其中“条件”是布尔类型表达式&#xff0c;“循环体”是一个代码块。while 表达式将按如下规则执行&#xff1a; 计算“条件”表达式&#xff0c;如果…

计算机毕业设计选题推荐-电缆行业生产管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

LabVIEW软件开发的未来是什么?

LabVIEW软件开发的未来展望可以从以下几个方面进行分析&#xff1a; 1. 与硬件集成的进一步增强 LabVIEW一贯以其与硬件的紧密集成而著称&#xff0c;未来这一优势将进一步得到强化。随着物联网&#xff08;IoT&#xff09;设备、工业4.0和智能制造的发展&#xff0c;LabVIEW将…

Mipi SoundWire Spec 详解4.2~4.3

目录 4.2 低层特性 4.2.1 物理接口 4.2.1.1 信号拓扑 4.2.1.2 多数据通道 4.2.1.3 高性能PHY 4.2.2 数据编码 4.2.3 物理信号值和逻辑信号值的术语 4.2.4 对开发和测试低级功能的支持 4.3 控制特性 4.3.1 比特流与帧结构 4.3.1.1 控制字与带宽权衡 4.3.2 同步 4.3.…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 8月8日,星期四

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年8月8日 星期四 农历七月初五 1、 财政部预拨4.65亿元资金支持辽宁、吉林等7省&#xff08;市&#xff09;开展应急抢险救灾工作。 2、 2024年“三区”人才支持计划发布&#xff1a;全国将选派15952名教师赴“三区”。 3…