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

news2025/1/19 20:39:21

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

文章链接:组合总和        组合总和II        分割回文串

视频链接:组合总和        组合总和II        分割回文串

目录

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

1. LeetCode 39. 组合总和

1.1 思路

1.2 代码

2. LeetCode 40. 组合总和 II

2.1 思路

2.2 代码

3. LeetCode 131. 分割回文串

3.1 思路

3.2 代码


1. LeetCode 39. 组合总和

1.1 思路

  1. 这题跟上面那些组合的题目的区别在于可以重复取数,而且这题抽象出来的树形结构的深度是由和来限定的。这里我们举例,数组[2,5,3],和为4,那么我们取了2之后,子集合是[2,5,3],因为可以重复取数,后续接着取。然后如果在第一层取5时(此时取2的路径已经走完了),子集合就是[5,3]了,这时如果再把2带上就会得到重复的组合,这也是startIndex控制的地方。这题和上面做个的类似组合问题的差别就是子集合是把已经选过的元素带上的,这样才可达到可重复选取的目的
  2. 定义两个全局变量,二维数组result存最终结果,path存放当前的单次结果。
  3. 回溯算法的返回值和参数:返回值void,参数candidates,target,sum统计path里的和,startIndex控制子集合的初始位置
  4. 终止条件:如果sum>target就return;如果sum==target就把path加入result中,然后return
  5. 单层搜索的逻辑:for循环(int i=startIndex; i<candidates.length; i++)。
  6. 注意:这里的for循环中startIndex第一次传入时是从0开始的,就是大家最常见的数组遍历方式,为什么与上面做过的那些组合的for循环不一样呢?因为上面的题取的元素都是1到9这样子的,要从1开始取之类的,而本题的数组遍历是通过下标遍历的,因此有点不一样
  7. 接上述的for循环,然后path.add(数组[i]);sum+=数组[i];然后就递归的过程backtracking(candidates, target, sum, i)。注意这里传入的是i不是startIndex,因为传入startIndex就会一直是0,而i会在for循环里递增,那样才是正确的遍历顺序。而这里不用i+1就是可以重复取数的关键,下一层递归依然可以取同一个数
  8. 然后是回溯的过程,path.removeLast()去除最后一个元素也就是刚刚加入的元素,这样才能往树的右边搜索,并且sum-=数组[i]
  9. 剪枝优化:通常是在for循环里做文章的,剪枝就是剪去多余的分支,分支就体现在for循环上,原理就是让其少循环,上述的sum>target就是不进入循环,下面的方式就是提前退出循环。对于本题,还是上面的例子[2 ,5, 3],先取出2和5发现已经大于4了,如果我们先排序数组,变成[2, 3, 5]那么取到2和3发现已经大于4了,那么后续的5就可以不用去搜索了。
  10. 这个操作就是写在for循环里的,如果sum+数组[i]>target了就直接break跳出循环,这就是把此树右边的分支剪去了。

1.2 代码

//
// 剪枝优化
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(candidates); // 先进行排序
        backtracking(res, new ArrayList<>(), candidates, target, 0, 0);
        return res;
    }

    public void backtracking(List<List<Integer>> res, List<Integer> path, int[] candidates, int target, int sum, int idx) {
        // 找到了数字和为 target 的组合
        if (sum == target) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = idx; i < candidates.length; i++) {
            // 如果 sum + candidates[i] > target 就终止遍历
            if (sum + candidates[i] > target) break;
            path.add(candidates[i]);
            backtracking(res, path, candidates, target, sum + candidates[i], i);
            path.remove(path.size() - 1); // 回溯,移除路径 path 最后一个元素
        }
    }
}

2. LeetCode 40. 组合总和 II

2.1 思路

  1. 这题和上述的组合类的题目的区别在于题目给的数组是有重复元素的,上面做过的是没有的,而且题目要求组合中不可以有重复的组合,即答案里的数组可以出现[1,1,6]但不同出现[1,1,6],[1,1,6],因此要去重
  2. 举个例子,数组[10, 1, 2, 7, 6, 1, 5],和为8,这里就会出现两个1和7组成组合然后和为8,因此要做去重,而且题目要求同一个元素在单个组合中只能使用一次。大逻辑就是取过的元素就不要再取了(好像是废话)。
  3. 本题科普两个概念:树层去重,树枝去重。我们要考虑这两个维度,从这两个上考虑去重
  4. 我们需要定义一个boolean数组used,长度和题目给的数组是一样大的,如果对应下标的数组元素使用过就定义为true否则为false。再举个例子如上图所示,排好序的数组[1, 1, 2],和为3。取1后used[0]=true,子集合为[1, 2],以此类推,同样需要startIndex控制起始位置,for循环往右搜索的过程,起始位置都是不断+1的,只是我们要用used标记取过的数为true
  5. 树枝去重:树枝上我们第一层取了1之后,第二层也是可以取1的,这两个1不会是同一个元素的,因为传入的是i+1
  6. 树层去重:树层上我们左边第一个分支取1了,第二个分支还取1虽然不是同一个元素的,但是我们都是从这个1后面开始取的,也就是比如[1, 1, 2,......]这里的第一个1和第二个1虽然不同,但是后面的是相同的,我们可能取后面的元素,就可能重复了。因此关键在树层去重
  7. 定义两个全局变量result存放全部结果、path存放单个结果
  8. 回溯函数的参数和返回值:返回值void,参数candidates,target,sum计算path里的和,startIndex控制子集合的起始位置,为了防止组合里出现重复元素,boolean数组used标记哪个元素用过。不过我们在进入这个回溯函数之前要在主函数先给数组排序,因为去重是在这个前提下进行的
  9. 终止条件:如果sum>target就return。如果sum==target就把path加入result中,然后return
  10. 单层搜索的逻辑:for(int i=startIndex;i<candidates.length;i++)。首先是树层去重的逻辑,如果(i>0&&candidates[i]==candidates[i-1]&&!used[i-1])避免i-1为负数,首先要求i>0,然后是如果前后两个元素相等了就不能再取了,不然就重复了,然后下一个条件是前一个元素不能用过,这里是为了往下递归时,举例,取了一个1后,还能取多个1,这里的多个1不是同一个1,只是数值相同,这个条件不是为了树层去重的,而是为了取数找到结果。如果符合条件就continue跳过此次循环,继续往后取。
  11. 然后就是继续收集元素的过程path.add(数组[i]);sum+=数组[i],used[i]=true;然后就递归的过程backtracking(candidates, target, sum, i+1, used)。然后就是回溯的过程,path.removeLast()去除最后一个元素也就是刚刚加入的元素,这样才能往树的右边搜索,并且sum-=数组[i],used[i]=false。这里又变为false是为了往右搜索,逻辑和第一层往右搜索的原因是一样的

2.2 代码

//
class Solution {
  LinkedList<Integer> path = new LinkedList<>();
  List<List<Integer>> ans = new ArrayList<>();
  boolean[] used;
  int sum = 0;

  public List<List<Integer>> combinationSum2(int[] candidates, int target) {
    used = new boolean[candidates.length];
    // 加标志数组,用来辅助判断同层节点是否已经遍历
    Arrays.fill(used, false);
    // 为了将重复的数字都放到一起,所以先进行排序
    Arrays.sort(candidates);
    backTracking(candidates, target, 0);
    return ans;
  }

  private void backTracking(int[] candidates, int target, int startIndex) {
    if (sum == target) {
      ans.add(new ArrayList(path));
    }
    for (int i = startIndex; i < candidates.length; i++) {
      if (sum + candidates[i] > target) {
        break;
      }
      // 出现重复节点,同层的第一个节点已经被访问过,所以直接跳过
      if (i > 0 && candidates[i] == candidates[i - 1] && !used[i - 1]) {
        continue;
      }
      used[i] = true;
      sum += candidates[i];
      path.add(candidates[i]);
      // 每个节点仅能选择一次,所以从下一位开始
      backTracking(candidates, target, i + 1);
      used[i] = false;
      sum -= candidates[i];
      path.removeLast();
    }
  }
}

3. LeetCode 131. 分割回文串

3.1 思路

  1. 大概是分两步,先求出所有分割后的结果,在叶子节点的位置,然后在这个位置判断是否都是回文
  2. 回溯函数的参数和返回值:返回值void。要先定义两个全局变量,一维数组path收集路径即单个结果,存放的是字符串,二维数组result收集全部结果。函数参数:字符串s,startIndex控制取了一个字符后,剩下的要在这个字符后面的字符串中取
  3. 终止条件:上面参数startIndex就相当于我们图里画是那条切割的竖线“|”,当这条线“|”到了最后的位置说明到叶子节点了,也就是要处理的结果了。if(startIndex==s.length())就是到了叶子节点,然后result里加入path。我们这里是把判断是否回文的逻辑放在了“单层搜索的逻辑”中了,如果不是回文就不会进行下一层的递归
  4. 单层搜索的逻辑:for(int i=startIndex;i<s.length();i++),这里我们要先判断切割的子串是否为回文串,是才加入到path。上面说过startIndex就是我们切割的线,那么[startIndex,i]这个左闭右闭的区间,就是切割的子串。这里startIndex在for循环里是一个固定的值,i是不断++的,因此他们之间的范围包括两端就是我们要搜索的范围。
  5. 这里定义个函数isPalindrome(s, startIndex, end)分别表示字符串,起始位置,终止位置,来判断这个区间内是否是回文的。如果是回文的,就path.add(子串),否则就continue跳过此次循环。
  6. 然后是递归的逻辑backtracking(s,i+1)为什么i+1是因为切割过的不能重复切割。然后是回溯的过程,path.removeLast(),继续向右搜索。
  7. 判断函数就是用双指针,同时向中间遍历,如果都相等就是回文子串

3.2 代码

//
class Solution {
    List<List<String>> lists = new ArrayList<>();
    Deque<String> deque = new LinkedList<>();

    public List<List<String>> partition(String s) {
        backTracking(s, 0);
        return lists;
    }

    private void backTracking(String s, int startIndex) {
        //如果起始位置大于s的大小,说明找到了一组分割方案
        if (startIndex >= s.length()) {
            lists.add(new ArrayList(deque));
            return;
        }
        for (int i = startIndex; i < s.length(); i++) {
            //如果是回文子串,则记录
            if (isPalindrome(s, startIndex, i)) {
                String str = s.substring(startIndex, i + 1);
                deque.addLast(str);
            } else {
                continue;
            }
            //起始位置后移,保证不重复
            backTracking(s, i + 1);
            deque.removeLast();
        }
    }
    //判断是否是回文串
    private boolean isPalindrome(String s, int startIndex, int end) {
        for (int i = startIndex, j = end; i < j; i++, j--) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }
}

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

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

相关文章

leetcode 5

leetcode 5 题目是通过枚举字符串&#xff0c;然后判断是否子字符串满足回文。 引用传递和值传递相比&#xff0c;引用传递可以减少内存空间。提高代码运行效率。 https://www.cnblogs.com/yanlingyin/archive/2011/12/07/2278961.html

ZKP4.1 SNARKs via Interactive Proofs (Justin Thaler)

ZKP学习笔记 ZK-Learning MOOC课程笔记 Lecture 4: SNARKs via Interactive Proofs (Justin Thaler) 4.1 Interactive Proofs: Motivation and Model Interactive Proofs P solves problem, tells V the answer. Then they have a conversation.P’s goal: convince V the …

微信小程序一键获取位置

需求 有个表单需要一键获取对应位置 并显示出来效果如下&#xff1a; 点击一键获取获取对应位置 显示在 picker 默认选中 前端 代码如下: <view class"box_7 {{ showChange1? change-style: }}"><view class"box_11"><view class"…

Excel·VBA单元格区域数据对比差异标记颜色

之前的一篇博客《ExcelVBA单元格重复值标记颜色》&#xff0c;是对重复的整行标记颜色 而本文是按行对比2个单元格区域的数据&#xff0c;并对有差异的区域&#xff08;一个单元格区域有的&#xff0c;而另一个单元格区域没有的&#xff09;标记颜色&#xff0c;且只要存在任意…

杨辉三角按列求和

假设求杨辉三角这一列 我们考虑这个格子&#xff1a; 然后对其不断展开 综上&#xff1a; ∑ i 0 n ( i k ) ( n 1 k 1 ) \sum_{i0}^n\binom i k\binom {n1}{k1} i0∑n​(ki​)(k1n1​) ∑ i l r ( i k ) ( r 1 k 1 ) − ( l k 1 ) \sum_{il}^r\binom i k\binom{r1}{k…

Kafka存取原理与实现分析,打破面试难关

系列文章目录 上手第一关&#xff0c;手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么&#xff0c;以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析&#xff0c;打破面试难关 系列文章目录一、主题与分区1. 模型…

每日一博 - Code如何被发布到生产环境

文章目录 概述Flow 概述 关于公司如何将代码发布到生产环境的是一个什么样的流程呢&#xff1f; 下面的图示展示了典型的工作流程。 步骤 1&#xff1a;流程始于产品负责人根据需求创建用户故事。步骤 2&#xff1a;开发团队从积压工作中挑选用户故事&#xff0c;将它们放入…

Flutter——最详细(CustomScrollView)使用教程

CustomScrollView简介 创建一个 [ScrollView]&#xff0c;该视图使用薄片创建自定义滚动效果。 [SliverList]&#xff0c;这是一个显示线性子项列表的银子列表。 [SliverFixedExtentList]&#xff0c;这是一种更高效的薄片&#xff0c;它显示沿滚动轴具有相同范围的子级的线性列…

pycharm操作git

pycharm操作git 之前用命令做的所有操作&#xff0c;使用pychrm点点就可以完成 克隆代码 上方工具栏Git ⇢ \dashrightarrow ⇢ Clone ⇢ \dashrightarrow ⇢ 填写地址&#xff08;http、ssh&#xff09; 提交到暂存区&#xff0c;提交到版本库&#xff0c;推送到远程 直接…

IOday7

A进程 #include <head.h> int main(int argc, const char *argv[]) {pid_t cpidfork();if(cpid>0)//父进程向管道文件2写{ int wfd;if((wfdopen("./myfifo2",O_WRONLY))-1){ERR_MSG("open");return -1;} char buf[128]"";while(1){bze…

ps或游戏提示d3dcompiler_47.dll缺失怎么修复?常见的修复方法总结

在当今这个信息化的时代&#xff0c;计算机已经成为我们生活和工作中不可或缺的一部分。然而&#xff0c;随着软件的不断更新和升级&#xff0c;一些技术问题也时常困扰着我们。其中&#xff0c;d3dcompiler_47.dll缺失就是一个常见的问题。本文将详细介绍五种修复方案&#xf…

CART(classification and regression tree)

基尼指数 在分类问题中&#xff0c;假设有K个类&#xff0c;样本点属于第k类的概率为pk&#xff0c;则概率分布的基尼指数定义为 Gini指数越小表示集合的纯度越高&#xff0c;反之&#xff0c;集合越不纯 CART CART分类树默认使用基尼指数选择最优特征 常见数构建算法&#…

从零实现FFmpeg6.0+ SDL2播放器

FFmpeg6.0开发环境搭建播放器代码框架分析解复用模块开发实现包队列和帧队列设计音视频解码线程实现SDL2音频声音输出SDL2视频画面渲染-YUV显示音视频同步-基于音频 地址: https://xxetb.xet.tech/s/3NWJGf

Django项目配置

1 项目准备 1.1 创建test数据库&#xff0c;并导入数据&#xff0c;生成对应的表 登录数据库create database test;use test;导入数据创建表:source D:/Demo.sql; 1.2 安装Django及驱动程序: 安装django&#xff1a; pip install Django3.2.22 安装好后可使用命令&#xf…

家中种绿植有什么风水讲究?

现在越来越多的人&#xff0c;都居住在小区高楼里&#xff0c;与绿植的接触也越来越少&#xff0c; 因此&#xff0c;很多人会选择在自己家中种上几株绿植。在家里种植植物&#xff0c;不仅美观&#xff0c;陶冶情操&#xff0c;还能净化空气&#xff0c;为家中增添好的风水。 …

论文阅读:Efficient Point Cloud Segmentation with Geometry-Aware Sparse Networks

来源&#xff1a;ECCV2022 链接&#xff1a;Efficient Point Cloud Segmentation with Geometry-Aware Sparse Networks | SpringerLink 0、Abstract 在点云学习中&#xff0c;稀疏性和几何性是两个核心特性。近年来&#xff0c;为了提高点云语义分割的性能&#xff0c;人们提…

数据结构--B树

目录 回顾二叉查找树 如何保证查找效率 B树的定义 提炼 B树的插入和删除 概括B树的插入方法如下 B树的删除 导致删除时&#xff0c;结点不满足关键字的个数范围时&#xff08;需要借&#xff09; 如果兄弟不够借&#xff0c;需要合体 回顾B树的删除 B树 B树的查找 …

python二次开发Solidworks:圆+样条曲线草图

以下代码实现在Solidworks中构建草图&#xff0c;在草图中绘制了一个圆和一根样条曲线&#xff0c;并实现全约束。 import numpy as np import win32com.client as win32 import pythoncom def vtPoint(x, y, z):# 坐标点转化为浮点数return win32.VARIANT(pythoncom.VT_ARRAY…

分享《泰坦陨落2》缺少msvcr120.dll的解决方法,亲测有效的5个修复方法

自我接触《泰坦陨落2》这款游戏以来&#xff0c;我深深地被它精彩的战斗场景、丰富的剧情设定以及独特的角色设计所吸引。然而&#xff0c;在一次游戏过程中&#xff0c;我遭遇了一个前所未有的问题——缺少msvcr120.dll文件&#xff0c;导致游戏无法正常运行。 一、缺少msvcr1…

【Java 进阶篇】深入了解 JavaScript 的 innerHTML 属性

JavaScript 是前端开发中不可或缺的一部分&#xff0c;它为我们提供了丰富的工具和技术&#xff0c;以便更好地操作和交互HTML页面。在本文中&#xff0c;我们将重点介绍JavaScript中的 innerHTML 属性&#xff0c;它是DOM&#xff08;文档对象模型&#xff09;的一部分&#x…