代码随想录算法训练营第二十七天|39.组合总和、40.组合总和II、131.分割回文串

39.组合总和

思路:

本题和77.组合 ,216.组合总和III的区别是:本题没有数量要求,可以无限重复,但是有总和的限制,所以间接的也是有个数的限制。

本题搜索的过程抽象成树形结构如下:

39.组合总和

注意图中叶子节点的返回条件,因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!

而在77.组合和216.组合总和III中都可以知道要递归K层,因为要取k个元素的组合。

剪枝优化:

在这个树形结构中:

39.组合总和

以及上面的版本一的代码大家可以看到,对于sum已经大于target的情况,其实是依然进入了下一层递归,只是下一层递归结束判断的时候,会判断sum > target的话就返回。

其实如果已经知道下一层的sum会大于target,就没有必要进入下一层递归了。

那么可以在for循环的搜索范围上做做文章了。

对总集合排序之后,如果下一层的sum(就是本层的 sum + candidates[i])已经大于target,就可以结束本轮for循环的遍历

39.组合总和1

代码:

未剪枝优化

class Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:result = [] # 初始化一个空列表,用于存储所有满足条件的组合path = [] # 初始化一个空列表,用于存储当前正在构建的组合self.backtracking(candidates, target, 0, 0, path, result) # 调用回溯函数开始搜索return result # 返回所有满足条件的组合def backtracking(self, candidates, target, total, startIndex, path, result):if total > target: # 如果当前组合的总和大于目标值,返回上一层returnif total == target: # 如果当前组合的总和等于目标值,则将该组合添加到结果列表中result.append(path[:]) # 注意这里使用了切片操作,是为了避免直接添加path的引用,从而确保添加到result中的是组合的一个副本returnfor i in range(startIndex, len(candidates)): # 从startIndex开始遍历candidates数组total += candidates[i] # 将当前数字添加到当前组合的总和中path.append(candidates[i]) # 将当前数字添加到当前正在构建的组合中self.backtracking(candidates, target, total, i, path, result) # 递归调用回溯函数,继续搜索。startIndex不用i+1了,表示可以重复使用相同的数total -= candidates[i] # 回溯,将当前数字从当前组合的总和中减去path.pop() # 回溯,将当前数字从当前正在构建的组合中移除

剪枝优化 

class Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:result = []path = []candidates.sort() # 需要排序self.backtracking(candidates, target, 0, 0, path, result)return resultdef backtracking(self, candidates, target, total, startIndex, path, result):if total == target:result.append(path[:])returnfor i in range(startIndex, len(candidates)):if total + candidates[i] > target: # 剪枝操作breaktotal += candidates[i]path.append(candidates[i])self.backtracking(candidates, target, total, i, path, result)total -= candidates[i]path.pop()
  • 时间复杂度: O(n * 2^n),注意这只是复杂度的上界,因为剪枝的存在,真实的时间复杂度远小于此
  • 空间复杂度: O(target)

40. 组合总和 II

思路:

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

一些同学可能想了:我把所有组合求出来,再用set或者map去重,这么做很容易超时!所以要在搜索的过程中就去掉重复组合。

所谓去重,其实就是使用过的元素不能重复选取。

都知道组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。

回看一下题目,元素在同一个组合内是可以重复的,怎么重复都没事,但两个组合不能相同。

所以我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重

为了理解去重我们来举一个例子,candidates = [1, 1, 2], target = 3,(方便起见candidates已经排序了)强调一下,树层去重的话,需要对数组排序!

选择过程树形结构如图所示:

40.组合总和II

直接用startIndex来去重也是可以的, 就不用used数组了 

 if i > startIndex and candidates[i] == candidates[i - 1]

 可以让同一层级,不出现相同的元素(就是重复出现的只保留最左侧的一支树枝,其他的就去重)

代码:

class Solution:def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:result = [] # 初始化结果列表,用于存放所有可能的组合path = [] # 初始化当前路径列表,用于构建当前的组合candidates.sort() # 对候选数字列表进行排序,以便处理重复数字时能够跳过它们self.backtracking(candidates, target, 0, 0, path, result) # 调用回溯方法开始搜索所有可能的组合return result # 返回所有有效的组合列表def backtracking(self, candidates, target, total, startIndex, path, result):if total == target: # 如果当前路径的总和等于目标值,则将当前路径添加到结果列表中result.append(path[:])returnfor i in range(startIndex, len(candidates)): # 遍历候选数字列表,从startIndex开始if i > startIndex and candidates[i] == candidates[i - 1]: # 去重。如果当前数字和前一个数字相同,并且不是第一个数字,则跳过,以避免重复的组合continueif total + candidates[i] > target: # 剪枝。如果加上当前数字后的总和已经超过了目标值,则结束循环breaktotal += candidates[i] # 将当前数字添加到当前组合的总和中path.append(candidates[i]) # 将当前数字添加到当前正在构建的组合中self.backtracking(candidates, target, total, i + 1, path, result) # 递归调用回溯函数,继续搜索。更新startIndex为i+1,确保不会重复使用相同的数字total -= candidates[i] # 回溯,将当前数字从当前组合的总和中减去path.pop() # 回溯,将当前数字从当前正在构建的组合中移除
  • 时间复杂度: O(n * 2^n)
  • 空间复杂度: O(n)

131. 分割回文串

思路:

本题这涉及到两个关键问题:

  1. 切割问题,有不同的切割方式
  2. 判断回文

这种题目,想用for循环暴力解法,可能都不那么容易写出来,所以要换一种暴力的方式,就是回溯。 

一些同学可能想不清楚 回溯究竟是如何切割字符串呢?

我们来分析一下切割,其实切割问题类似组合问题

例如对于字符串abcdef:

  • 组合问题:选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中再选取第三个.....。
  • 切割问题:切割一个a之后,在bcdef中再去切割第二段,切割b之后在cdef中再切割第三段.....。

所以切割问题,也可以抽象为一棵树形结构,如图:

131.分割回文串

递归用来纵向遍历,for循环用来横向遍历,切割线(就是图中的红线)切割到字符串的结尾位置,说明找到了一个切割方法。

此时可以发现,切割问题的回溯搜索的过程和组合问题的回溯搜索的过程是差不多的。

 

代码:

class Solution:def partition(self, s: str) -> List[List[str]]:result = [] # 初始化一个空列表,用于存储所有可能的回文子串组合path = [] # 初始化一个空列表,用于在回溯过程中存储当前的回文子串组合self.backtracking(s, 0, path, result) # 调用回溯方法开始搜索return result # 返回所有可能的回文子串组合def backtracking(self, s, start_index, path, result ):if start_index == len(s): # 如果已经遍历(切割)到字符串的末尾result.append(path[:]) # 将当前的回文子串组合添加到结果列表中(注意这里使用了path的副本,避免后续修改影响结果)return # 结束递归,返回上一层for i in range(start_index, len(s)): # 从start_index开始遍历字符串的剩余部分if s[start_index: i + 1] == s[start_index: i + 1][::-1]: # 检查从start_index到i的子串是否是回文path.append(s[start_index:i+1]) # 如果是回文,则将其添加到当前的回文子串组合中self.backtracking(s, i+1, path, result) # 递归调用,从下一个位置继续搜索。注意切割过的位置,不能重复切割,所以,backtracking(s, i + 1); 传入下一层的起始位置为i + 1。path.pop() # 回溯,移除最后一个添加的回文子串,尝试其他可能的组合

时间复杂度:O(N * 2 ^ N),因为总共有O(2^N)种分割方法,每次分割都要判断是否回文需要 O(N) 的时间复杂度。
空间复杂度:O(2 ^ N),返回结果最多有O(2 ^ N)种划分方法。

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习笔记——C语言基本概念栈和队列——(14)

本次笔记是C语言的最后学习内容。 1、栈 特点:先进后出 堆栈又名栈( stack) , 它是一种运算受限的线性表。 限定仅在表尾进行插入和删除操作的线性表。 这一端被称为栈顶, 相对地, 把另一端称为栈底。 向…

[大模型]Baichuan2-7B-chat langchain 接入

Baichuan2-7B-chat langchain 接入 这篇主要讲 Baichuan2-7B-chat 如何对接Langchain中 langchain.llms.base 的 LLM 模块,并且提供一个快捷搭建向量数据库、Agent等多功能的Langchain应用的部署方案;关于如何具体对接向量数据库和gradio的部分请参考in…

排序之归并排序

代码 先上代码,根据代码将归并排序的整个过程。 class Solution {public int[] sortArray(int[] nums) {int[] tmp new int[nums.length];merge(nums, 0, nums.length - 1, tmp);return nums;}private void merge(int[] nums, int left, int right, int[] tmp){if…

kmeans聚类sklearn实现(Python实验)

Kmeans毫无疑问,好用又“便宜”的算法,经常在很多轻量化场景中实现。所谓的“聚类”(Clustering),就是通过欧氏距离找哪些点构成一个簇。假设我们空间中有一堆点,通过肉眼大概可以看出有两簇,思…

OpenHarmony实战开发-如何实现图片缩放效果。

介绍 图片预览在应用开发中是一种常见场景,在诸如QQ、微信、微博等应用中均被广泛使用。本模块基于Image组件实现了简单的图片预览功能。 使用说明: 双指捏合对图片进行缩放双击图片进行图片的大小切换,在放大状态下,双击可恢复…

TensorFlow 1.x的学习

.为什么还有很多人都选择使用TensorFlow 1.x 兼容性问题: TensorFlow 1.x在一些旧项目中已经得到了广泛应用,这些项目可能依赖于1.x版本的特定API或行为。升级到2.x可能需要大量的代码修改和测试工作,对于一些已经稳定运行的项目,维护者可能…

内网渗透-内网环境下的横向移动总结

内网环境下的横向移动总结 文章目录 内网环境下的横向移动总结前言横向移动威胁 威胁密码安全 威胁主机安全 威胁信息安全横向移动威胁的特点 利用psexec 利用psexec.exe工具msf中的psexec 利用windows服务 sc命令 1.与靶机建立ipc连接2.拷贝exe到主机系统上3.在靶机上创建一个…

聚观早报 | 哪吒L上市定档;iPhone 16最新高清渲染图

聚观早报每日整理最值得关注的行业重点事件,帮助大家及时了解最新行业动态,每日读报,就读聚观365资讯简报。 整理丨Cutie 4月10日消息 哪吒L上市定档 iPhone 16最新渲染图 华为太空表与问界M9联动 蔚来万里长城加电风景线正式贯通 Red…

基于Springboot+Vue的Java项目-房产销售系统(附演示视频+源码+LW)

大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:Java毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计 &am…

ubuntu16.04安装Eclipse C/C++

1.安装 JDK 官网源码安装 首先打开JDK官网,JDK1.8的下载网址为:https://www.oracle.com/cn/java/technologies/downloads/#java8-windows,进入到网址如下图所示: 向下滑动到 JDK1.8的下载界面,如下图所示&#xff1a…

戏作打油诗《无知》

笔者经营多年的《麻辣崇州论坛》,半月前突被攻击我在“霸屏”,没处讲理,特戏作打油诗《无知》一首,为那个无理取闹、砸我“麻辣崇州论坛”的无知小儿画像如下。 请点击链接,一目了然:崇州论坛-麻辣社区 没…

2024-04-15_[UPnP]:详细解析

UPnP 一、论文阅读 1.2 Theory 1.2.1 Geometry of the absolute pose problem α i f i v i R p i t , i 1.. n . \alpha_i \mathbf{f}_i \mathbf{v}_i \mathbf{R} \mathbf{p}_i \mathbf{t} ,i1..n. αi​fi​vi​Rpi​t,i1..n. 其中: P i ∈ R 3 P_i \i…