代码随想录第27天|39. 组合总和,40.组合总和II,131.分割回文串

39. 组合总和

分析这道题的搜索过程如下:

 

因为这道题没有限制要搜索几层,所以可以一直搜索直到sum==target或者sum>target就return

回溯三部曲

1.递归函数参数

 本题还需要startIndex来控制for循环的起始位置,对于组合问题,什么时候需要startIndex呢?

果是一个集合来求组合的话,就需要startIndex,例如:77.组合 (opens new window),216.组合总和III (opens new window)。

如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex,例如:17.电话号码的字母组合

2.终止条件

从叶子节点可以清晰看到,终止只有两种情况,sum大于target和sum等于target。

注意sum等于target的时候,需要收集结果

3.单层搜索的逻辑

单层for循环依然是从startIndex开始,搜索candidates集合。

注意本题和77.组合 (opens new window)、216.组合总和III (opens new window)的一个区别是:本题元素为可重复选取的

   //递归逻辑for(int i=startIndex;i<candidates.length;i++){sum+=candidates[i];path.add(candidates[i]);backtracking(candidates,target,sum,i);//不用i+1,因为这道题是可以重复读取元素的sum-=candidates[i];//回溯path.removeLast();//移除最新加入的元素}

总体代码

class Solution {List<List<Integer>> res=new ArrayList<List<Integer>>();LinkedList<Integer> path=new  LinkedList<Integer>();//无限制重复被选取//只要选取的元素总和超过target,就返回!public List<List<Integer>> combinationSum(int[] candidates, int target) {Arrays.sort(candidates);//可要可不要backtracking(candidates,target,0,0);return res;}public void backtracking(int[] candidates, int target,int sum,int startIndex){//递归终止条件// sum==target或者sum>target,就returnif(sum==target){res.add(new ArrayList<>(path));}if(sum>target){return;}//递归逻辑for(int i=startIndex;i<candidates.length;i++){sum+=candidates[i];path.add(candidates[i]);backtracking(candidates,target,sum,i);//不用i+1,因为这道题是可以重复读取元素的sum-=candidates[i];//回溯path.removeLast();//移除最新加入的元素}}
}

4.剪枝优化

 

在递归逻辑中如果sum>target了,那就不需要再继续遍历了,所以需要剪枝的部分是

  for(int i=startIndex;i<candidates.length&&sum<=target;i++){

40.组合总和II

这道题因为其数组candidates有重复元素,而要求返回的结果不能有重复的组合,所以相对于39.组合总和 (opens new window)难度提升了不少。

问题1.要去重的是哪里

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

强调一下,树层去重的话,需要对数组排序!

可以看到图中,每个节点相对于 39.组合总和 (opens new window)我多加了used数组,

用来记录同一树枝上的元素是否使用过。这个集合去重的重任就是used来完成的。

问题2.要去重的是“同一树层上的使用过”,如何判断同一树层上元素(相同的元素)是否使用过了呢。

如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明:前一个树枝(一条路径可以看成一个树枝),使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]

此时for循环里就应该做continue的操作。跳过当前遍历的candidates[i]

 

我在图中将used的变化用橘黄色标注上,可以看出在candidates[i] == candidates[i - 1]相同的前提(注意这个前提)下:

  • used[i - 1] == true,说明同一树枝candidates[i - 1]使用过(这是递归)
  • used[i - 1] == false,说明同一树层candidates[i - 1]使用过,因为这一部分会回溯才能到同一树层,所以used[i-1]会等于false(这是回溯)

问题3.可能有的录友想,为什么 used[i - 1] == false 就是同一树层呢?

因为同一树层,used[i - 1] == false 才能表示,当前取的 candidates[i] 是从 candidates[i - 1] 回溯而来的。而 used[i - 1] == true,说明是进入下一层递归,去下一个数,所以是树枝上,如图所示:

 回溯三部曲

1.递归函数参数

同39.组合总和 (opens new window),此题还需要加一个bool型数组used,用来记录同一树枝上的元素是否使用过。

2.终止条件

同39题

3.单层搜索的逻辑

如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]

实现代码

//candidates 中的每个数字在每个组合中只能使用 一次 。class Solution {List<List<Integer>> res=new ArrayList<>();LinkedList<Integer> path=new LinkedList<Integer>();boolean[] used;public List<List<Integer>> combinationSum2(int[] candidates, int target) {used=new boolean[candidates.length];Arrays.sort(candidates);//排序是为了让相同的元素聚集在一起,方便判断同一层是不是已使用过这个元素backtracking(candidates,target,0,0);return res;}public void backtracking(int[] candidates, int target,int sum,int startIndex){//1.终止条件if(sum==target){res.add(new ArrayList<>(path));//如果补new的话加入的path都是空集return;}if(sum>target){return;}//2.递归逻辑//for(int i=startIndex;i<candidates.length&&sum<target;i++){if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false){//同一树层不能返回相同的元素,要跳过continue;}used[i]=true;//标记下标为i的这个数字被使用了sum+=candidates[i];path.add(candidates[i]);backtracking(candidates,target,sum,i+1);//每个数字只能用1次,所以i+1要遍历下一个数字used[i]=false;//回溯sum-=candidates[i];//回溯path.removeLast();//移除path中最新加入的元素}}
}

131.分割回文串

问题1  回溯究竟是如何切割字符串呢?

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

 

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

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

 回溯三部曲

1.递归函数参数

本题递归函数参数还需要startIndex,因为切割过的地方,不能重复切割,和组合问题也是保持一致的。

在回溯算法:求组合总和(二) (opens new window)中我们深入探讨了组合问题什么时候需要startIndex,什么时候不需要startIndex。

2.终止条件

切割线切到了字符串最后面,说明找到了一种切割方法,此时就是本层递归的终止条件。

 

那么在代码里什么是切割线呢?

在处理组合问题的时候,递归参数需要传入startIndex,表示下一轮递归遍历的起始位置,这个startIndex就是切割线。

3.单层搜索的逻辑

来看看在递归循环中如何截取子串呢?

for (int i = startIndex; i < s.size(); i++)循环中,我们 定义了起始位置startIndex,那么 [startIndex, i] 就是要截取的子串。

首先判断这个子串是不是回文,如果是回文,就加入在vector<string> path中,path用来记录切割过的回文子串。

代码实现

class Solution {List<List<String>> res=new ArrayList<>();LinkedList<String> path=new LinkedList<>();public List<List<String>> partition(String s) {backtracking(s,0);return res;}public void backtracking(String s,int startIndex){//终止条件//如果起始的位置大于s的大小,说明已经找到一组分割方案了if(startIndex>=s.length()){res.add(new ArrayList<>(path));return;}// 搜索逻辑for(int i=startIndex;i<s.length();i++){if(isPalindrome(s,startIndex,i)){  //是回文,要添加String str = s.substring(startIndex, i + 1);path.add(str);}else{continue;//不是回文,说明这种分割不对,return}backtracking(s,i+1); //回溯,继续横向分割(参考树的搜索过程)path.removeLast();//回溯}}//判断是否为回文boolean isPalindrome(String s,int start,int end){for(int i=start,j=end;i<=j;i++,j--){if(s.charAt(i)!=s.charAt(j)){//return false;}}return true;}
}

总结默念:

回溯三部曲:

1.递归函数参数和返回值

2.递归终止条件

3.单层搜索的逻辑

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

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

相关文章

P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布

[NOIP2014 提高组] 生活大爆炸版石头剪刀布 题目描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头。如果两个人出拳一样&#xff0c;则不分胜负。在《生活大爆炸》第二季第 8 集中出现了一种石头剪刀布的升级版游戏。 升级版游戏在传统的石头剪刀布游戏的基础…

在ARM服务器上一键安装Proxmox VE(以在Oracle Cloud VPS上为例)(甲骨文)

前言 如题&#xff0c;具体用到的说明文档如下 virt.spiritlhl.net 具体流程 首先是按照说明&#xff0c;先得看看自己的服务器符不符合安装 Proxmox VE的条件 https://virt.spiritlhl.net/guide/pve_precheck.html#%E5%90%84%E7%A7%8D%E8%A6%81%E6%B1%82 有提到硬件和软…

CSS 选择器

前言 基础选择器 以下是几种常见的基础选择器。 标签选择器&#xff1a;通过HTML标签名称选择元素。 例如&#xff1a; p {color: red; } 上述样式规则将选择所有<p>标签 &#xff0c;并将其文字颜色设置为红色。 类选择器&#xff1a;通过类名选择元素。使用类选择…

课程项目设计--spring security--用户管理功能--宿舍管理系统--springboot后端

写在前面&#xff1a; 还要实习&#xff0c;每次时间好少呀&#xff0c;进度会比较慢一点 本文主要实现是用户管理相关功能。 前文项目建立 文章目录 验证码功能验证码配置验证码生成工具类添加依赖功能测试编写controller接口启动项目 security配置拦截器配置验证码拦截器 …

电脑上安装,多版本node

手上有一个vue3的项目&#xff0c;sass配置如下图所示&#xff1a; 安装了Python3.10和node 16.14.0&#xff0c;项目能正常install 跟run。 因工作需要&#xff0c;收上有一个vue2的项目&#xff0c;sass配置如下图所示&#xff1a; 执行npm intsall 的时候一直报Python2找不…

2022年12月 C/C++(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题:数组逆序重放 将一个数组中的值按逆序重新存放。例如,原来的顺序为8,6,5,4,1。要求改为1,4,5,6,8。 输入 输入为两行:第一行数组中元素的个数n(1<n<100),第二行是n个整数,每两个整数之间用空格分隔。 输出 输出为一行:输出逆序后数组的整数,每两个整数之间…

Java云原生框架Quarkus初探

Java云原生框架Quarkus初探 Quarkus 介绍 Quarkus 是一个云原生&#xff0c;容器优先的Java应用框架&#xff0c;它号称是超音速和亚原子的框架&#xff0c;主要特点是构建速度、启动速度快和占用资源少等特点。它为OpenJDK HotSpot和GraalVM量身定制&#xff0c; 根据Java库和…

常用消息中间件介绍

RocketMQ 阿里开源&#xff0c;阿里参照kafka设计的&#xff0c;Java实现 能够保证严格的消息顺序 提供针对消息的过滤功能 提供丰富的消息拉取模式 高效的订阅者水平扩展能力 实时的消息订阅机制 亿级消息堆积能力 RabbitMQ Erlang实现&#xff0c;非常重量级&#xff0c;更适…

Nevron 3DChart Crack,可视化界面在运行时可用

Nevron 3DChart Crack,可视化界面在运行时可用 3DChart使用OpenGL 3D图形引擎创建复杂的2D和3D图表&#xff0c;这些图表可以包含静态或动画图像。3DChart包括一个用于生成图表模板的独立应用程序和一个ASP服务器配置实用程序。该组件还包括一个专门设计用于与3DChart集成的工具…

java版本企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发

java版本企业电子招标采购系统源码Spring Cloud Spring Boot 二次开发 一、立项管理 1、招标立项申请 功能点&#xff1a;招标类项目立项申请入口&#xff0c;用户可以保存为草稿&#xff0c;提交。 2、非招标立项申请 功能点&#xff1a;非招标立项申请入口、用户可以保存为草…

Scratch 游戏 之 随机大地图生成教程

在很多生存 / 沙盒类游戏中&#xff0c;地图往往是随机生成的&#xff0c;例如&#xff1a;饥荒、我的世界等。那我们该如何在scratch中实现这一点呢&#xff1f; 在scratch中有两种办法可以实现——画笔和克隆体。我们这次先聊克隆体。 我们可以先将克隆体设置为方形的&#x…

打怪升级之从零开始的网络协议

序言 三个多月过去了&#xff0c;我又来写博客了&#xff0c;这一次从零开始学习网络协议。 总的来说&#xff0c;计算机网络很像现实生活中的快递网络&#xff0c;其最核心的目标&#xff0c;就是把一个包裹&#xff08;信息&#xff09;从A点发送到B点去。下面是一些共同的…

Linux学习之ssh和scp

ls /etc/ssh可以看到这个目录下有一些文件&#xff0c;而/etc/ssh/ssh_config是客户端配置文件&#xff0c;/etc/ssh/sshd_config是服务端配置文件。 cat -n /etc/ssh/sshd_config | grep "Port "可以看一下sshd监听端口的配置信息&#xff0c;发现这个配置端口是22…

git分支

一、引言 分支的命名规范以及管理方式对项目的版本发布至关重要&#xff0c;为了解决实际开发过程中版本发布时代码管理混乱、冲突等比较头疼的问题&#xff0c;我们将在文中阐述如何更好的管理代码分支。 二、总览&#xfeff; 从上图可以看到主要包含下面几个分支&#xff…

【是C++,不是C艹】 手把手带你实现Date类(附源码)

&#x1f49e;&#x1f49e;欢迎来到 Claffic 的博客&#x1f49e;&#x1f49e; &#x1f449; 专栏&#xff1a;《是C&#xff0c;不是C艹》&#x1f448; 前言&#xff1a; 恍惚间&#xff0c;已经两个月没更新了 &#xff08;&#xff1b;д&#xff40;&#xff09;ゞ 我忏…

Redis 持久化的手段有哪些 ?RDB 和 AOF 有什么区别 ?

目录 1. Redis 持久化的手段有哪些 2. RDB 和 AOF 有什么区别 2.1 RDB 持久化 2.2 AOF 持久化 2.2.1 AOF 持久化策略有哪些 3. 混合持久化是如何执行的&#xff08;了解&#xff09; 1. Redis 持久化的手段有哪些 Redis 持久化的手段有三种&#xff1a; 快照方式&#…

开源数据库Mysql_DBA运维实战 (总结)

开源数据库Mysql_DBA运维实战 &#xff08;总结&#xff09; SQL语句都包含哪些类型 DDL DCL DML DQL Yum 安装MySQL的配置文件 配置文件&#xff1a;/etc/my.cnf日志目录&#xff1a;/var/log/mysqld.log错误日志&#xff1a;/var/log/mysql/error.log MySQL的主从切换 查看主…

什么是程序化交易接口?执行三步曲是什么?

在股市中的发展过程中&#xff0c;通过不断的更新迭代&#xff0c;从手动交易到自动交易的过程就有了历史的蜕变&#xff0c;那么对于程序化交易接口&#xff08;Application Programming Interface, API&#xff09;其实就是指为程序化交易提供的一组定义和规范&#xff0c;允…

物联网工程应用实训室建设方案

一、物联网工程应用系统概述 1.1物联网工程定义 物联网工程&#xff08;Internet of Things Engineering&#xff09;是一种以信息技术&#xff08;IT&#xff09;来改善实体世界中人们生活方式的新兴学科&#xff0c;它利用互联网技术为我们的日常生活活动提供服务和增益&am…