代码随想录算法训练营day27 | 39. 组合总和,40.组合总和II,131.分割回文串

news2024/9/27 19:26:19

代码随想录算法训练营day27 | 39. 组合总和,40.组合总和II,131.分割回文串

  • 39. 组合总和
    • 解法一:回溯
    • 解法二:回溯+排序剪枝
  • 40.组合总和II
    • 解法一:回溯(使用used标记数组)
    • 解法一:回溯(不使用used标记数组)高效但更抽象
  • 131.分割回文串
    • 解法一:回溯
  • 总结


39. 组合总和

教程视频:https://www.bilibili.com/video/BV1KT4y1M7HJ/?vd_source=ddffd51aa532d23e6feac69924e20891
在这里插入图片描述
在这里插入图片描述
相比于之前的组合问题,本题树的深度取决于和是否超出target。

解法一:回溯

在这里插入图片描述

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backtracking(candidates, target, 0, 0);
        return result;
    }
    public void backtracking(int[] candidates, int target, int sum,int startIndex){
        if(sum>target)return;
        if(sum==target){
            result.add(new ArrayList<>(path));
            return;
        }

        for(int i = startIndex ; i<candidates.length ; i++){
            sum+=candidates[i];
            path.add(candidates[i]);
            backtracking(candidates, target, sum, i);//因为可以重复,下一层从当前的第 i 个数字开始遍历
            sum-=candidates[i];
            path.remove(path.size()-1);
        }
    }
}

解法二:回溯+排序剪枝

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

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates); // 剪枝操作1:先进行排序
        backtracking(candidates, target, 0, 0);
        return result;
    }
    public void backtracking(int[] candidates, int target, int sum,int startIndex){
        // 剪枝操作3:剪枝后大于的情况被剔除,不再需要终止条件if(sum>target)return;
        if(sum==target){
            result.add(new ArrayList<>(path));
            return;
        }

        // 剪枝操作2:如果当前sum和本层数据之和大于target,退出本层该数据后的遍历
        for(int i = startIndex ; i<candidates.length && sum + candidates[i] <= target ; i++){
            sum+=candidates[i];
            path.add(candidates[i]);
            backtracking(candidates, target, sum, i);//因为可以重复,下一层从当前的第 i 个数字开始遍历
            sum-=candidates[i];
            path.remove(path.size()-1);
        }
    }
}

40.组合总和II

教程视频:https://www.bilibili.com/video/BV12V4y1V73A/?spm_id_from=333.788&vd_source=ddffd51aa532d23e6feac69924e20891
在这里插入图片描述
在这里插入图片描述
【树枝去重】:candidates 中的每个数字在每个组合中只能使用 一次 。但本题中 candidates 包含重复元素,因此结果路径中可以用值相同的元素,只要它们对应的是candidates中的不同索引。因此向下伸展树枝时,每增一层,开始序号减一,这是树枝去重逻辑。
【数层去重】:在每层遍历中,如果同一树层上存在值相同的元素,它们中的第一个必定已经包含之后若干相同元素的所有分支,因此每层遍历的集合需要去重。

解法一:回溯(使用used标记数组)

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);//排序
        boolean[] used = new boolean[candidates.length];
        // System.out.println(used[0]);
        backtracking(candidates, target, 0, used);
        return result;
    }

    //startIndex表示每层遍历的起点,用于数层去重
    //used用于记录使用过的元素,可以判断组合是在树枝上还是数层上,用于保护树枝中值重复的情况
    public void backtracking(int[] candidates, int target,int startIndex, boolean[] used){
        if(target<0)return;
        if(target==0){
            result.add(new ArrayList<>(path));
            return;
        }

        for(int i=startIndex;i<candidates.length;i++){
            //树层去重
            if(i>0 && candidates[i]==candidates[i-1] && !used[i-1])continue;
            target-=candidates[i];
            path.add(candidates[i]);
            used[i]=true;
            backtracking(candidates,target,i+1,used);
            target+=candidates[i];
            path.remove(path.size()-1);
            used[i]=false;
        }

    }
}

解法一:回溯(不使用used标记数组)高效但更抽象

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);//剪枝1:排序
        backtracking(candidates, target, 0);
        return result;
    }

    //startIndex表示每层遍历的起点,用于数层去重
    //used用于记录使用过的元素,可以判断组合是在树枝上还是数层上,用于保护树枝中值重复的情况
    public void backtracking(int[] candidates, int target,int startIndex){
        // if(target<0)return;
        if(target==0){
            result.add(new ArrayList<>(path));
            return;
        }

        for(int i=startIndex;i<candidates.length && target-candidates[i]>=0;i++){
            //树层去重,跳过同一树层使用过的元素
            if(i>startIndex && candidates[i]==candidates[i-1])continue;
            target-=candidates[i];
            path.add(candidates[i]);
            backtracking(candidates,target,i+1);
            target+=candidates[i];
            path.remove(path.size()-1);
        }
    }
}

131.分割回文串

教程视频:https://www.bilibili.com/video/BV1c54y1e7k6/?spm_id_from=333.788&vd_source=ddffd51aa532d23e6feac69924e20891
在这里插入图片描述
这是一个切割问题,切割问题可以抽象为组合问题,用回溯法解决。
关于模拟切割线,其实就是index是上一层已经确定了的分割线,i是这一层试图寻找的新分割线

解法一:回溯

class Solution {
    List<List<String>> result = new ArrayList<>();
    List<String> path = new ArrayList<>();

    public List<List<String>> partition(String s) {
        backtracking(s, 0);
        return result;
    }

    public void backtracking(String s,int startIndex){
        //startIndex表示切割位置,如果切割位置等于s的大小,说明找到了一组分割方案
        if(startIndex==s.length()){
            result.add(new ArrayList<>(path));
            return;
        }

        for(int i=startIndex;i<s.length();i++){
            //判断是否是回文
            if(isPalindrome(s,startIndex,i)){
                //如果是回文子串,则记录
                path.add(s.substring(startIndex,i+1));
            }else{
                //如果不是回文串,剪去下层的递归,横向遍历同层的另一个数
                continue;
            }
            backtracking(s,i+1);
            path.remove(path.size()-1);
        }
    }

    //判断是否是回文串,区间是左闭右闭的
    public boolean isPalindrome(String s,int start,int end){
        while(start<end){
            if(s.charAt(start)!=s.charAt(end)){
                return false;
            }
            start++;
            end--;
        }
        return true;
    }
}

总结

1、在求和问题中,排序之后加剪枝是常见的套路!
2、组合总和II需要理清“树层去重”和“树枝去重”两个维度。
3、切割问题可以抽象为组合问题,用回溯法解决。关键在于用startIndex模拟分割线,确定递归终止条件。

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

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

相关文章

1688获取商品api接口

作为一名技术爱好者&#xff0c;我们总会遇到各种各样的技术问题&#xff0c;需要寻找合适的技术解决方案。而在互联网时代&#xff0c;我们可以快速通过搜索引擎获取丰富的技术资源和解决方案。然而&#xff0c;在不同的技术分享中&#xff0c;我们常常会遇到质量参差不齐的文…

虹科方案 | 视频和广播专业人士的存储和存档解决方案

虹科HK & Overland-Tandberg 为所有视频和广播工作流阶段提供全面的数字媒体存储解决方案组合&#xff0c;包括创建、复制、传输、存储、保护和归档数据和内容。 一、后期制作工作流程 后期制作是一个多任务过程&#xff0c;通过掌握处理原始视频和声音元素。 这个过程的前…

c++ 11标准模板(STL) std::vector (十)

定义于头文件 <vector> template< class T, class Allocator std::allocator<T> > class vector;(1)namespace pmr { template <class T> using vector std::vector<T, std::pmr::polymorphic_allocator<T>>; }(2)(C17…

adb bugreport 与adb shell getprop 详解

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c; 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;不想当开发的测试&#xff0c;不是一个好…

低代码行业未来如何?大家都真的看好低代码开发吗?

低代码行业未来如何&#xff1f;大家都真的看好低代码开发吗&#xff1f; 是否一定需要开发人员&#xff1f;低代码和无代码平台会取代传统编程吗&#xff1f;低代码/无代码真的是未来吗&#xff1f; 无疑是需要且重要的。今天就来解答为什么低/零代码工具越来越受欢迎&#xf…

第1章计算机系统漫游

文章目录 1、信息就是位上下文2、程序被其他程序翻译成不同的格式3、了解编译系统如何工作的益处4、处理器读并解释储存在存储器中的指令4.1 系统的硬件组成4.2 执行 hello 程序 5、高速缓存6、形成层次结构的存储设备7、操作系统管理硬件7.1 进程7.2 线程7.3 虚拟存储器7.4 文…

领英退出中国,谷歌Bard集成进安卓,陆奇最新演讲,HuggingFace网传遭禁|今日科技圈要闻

夕小瑶科技说 原创 作者 | 智商掉了一地、兔子酱 AI 新闻速递来咯&#xff01;搬好小板凳&#xff0c;一起了解近期发生了什么新鲜事~ 领英职场退出中国 领英是一个专注于职业发展、招聘和营销等方面的社交平台。Linkdein 官方公众号发布公告称&#xff0c;由于面临日趋激烈的…

Spring MVC——Rest风格

REST&#xff08;Representational State Transfer&#xff09; 当我们想表示一个网络资源的时候&#xff0c;可以使用两种方式: 我们分别用查询id为1的用户信息与保存用户信息举例传统风格资源描述形式 http://localhost/user/getById?id1http://localhost/user/saveUser RES…

第五届河南省CCPC河南省省赛题解+复盘

第五届河南省CCPC河南省省赛题解复盘 今年省赛相当有意思的一点&#xff0c;是20级第一次线下省赛&#xff0c;对于部分队也可能是最后一次&#xff0c;看队名就能看出来很多 考研就业的选手&#xff0c;一群老年人在这PK&#xff0c;氛围挺不错。 A - 小水獭游河南 — 签到 …

代码随想录算法训练营day28 | 93.复原IP地址,78.子集,90.子集II

代码随想录算法训练营day28 | 93.复原IP地址&#xff0c;78.子集&#xff0c;90.子集II 93.复原IP地址解法一&#xff1a;回溯 78.子集解法一&#xff1a;回溯&#xff08;单独处理空集&#xff09;解法二&#xff1a;回溯&#xff08;统一处理空集&#xff09; 90.子集II解法一…

Linux网络编程:socket、客户端服务器端使用socket通信(TCP)

socket socket&#xff08;套接字&#xff09;&#xff0c;用于网络中不同主机间进程的通信。 socket是一个伪文件&#xff0c;包含读缓冲区、写缓冲区。 socket必须成对出现。 socket可以建立主机进程间的通信&#xff0c;但需要协议&#xff08;IPV4、IPV6等&#xff09;…

NeurIPS 2022 | 正则化分子构象场

编译 | 于洲 今天我们介绍来自复旦大学的Lihao Wang以及其他来自字节跳动AI实验室与清华大学AI产业研究院的成员发布在NeurIPS 2022会议上的工作&#xff0c;该文章介绍了一种新方法——正则化分子构象场&#xff08;RMCF&#xff09;&#xff0c;用于从化学结构中预测最有利的…

为什么聊天机器人界面不是未来

​ 0-1之间有无限多种状态 比如&#xff1a;0 按时上下班&#xff0c;用固定时间长度获取价值1 创业&#xff0c;用非线性时间&#xff0c;获取真实价值0-1 之间有无限多种状态 shadow ChatBot目前的交互界面有非常多值得被改进的体验机会。最近看到一篇非常有启发性的文章&…

【JY】浅析基于性能的抗震分析方法——性能设计

【写在前文】 在阅读此文前&#xff0c;可先看下以下文章&#xff1a; 【JY】基于性能的抗震设计&#xff08;一&#xff09; 【JY】基于性能的抗震设计&#xff08;二&#xff09; 【JY|理念】结构概念设计之(设计理念进展) 【性能设计】 建筑结构通常使用弹性分析进行抗震设计…

ProbTransformer:应对RNA折叠等自然过程数据模糊的神秘力量

编译 | 于洲‍ 今天我们介绍来自德国弗赖堡大学计算机科学系的Jrg K.H. Franke, Frederic Runge以及Frank Hutter发表在NeurIPS 2022会议上的工作&#xff0c;该文章介绍了一种新颖的基于概率的神经网络架构ProbTransformer&#xff0c;它是Transformer生态系统的一种层级增强&…

wangzherongyao PMO

感谢【五一节】大家的相遇&#xff0c;总结下。 2023年05月02日&#xff0c;【第一组】组队开黑 我总结了下这天为什么打的那么好&#xff0c;首先赛季初段位在王者附近&#xff0c;大家心态重视程度也高&#xff0c;不轻敌&#xff0c;也不盲目&#xff0c;运营好兵线一步一步…

springboot2集成knife4j(swagger3)

springboot2集成knife4j&#xff08;swagger3&#xff09; springboot2集成knife4j&#xff08;swagger3&#xff09; 环境说明集成knife4j 第一步&#xff1a;引入依赖第二步&#xff1a;编写配置类第三步&#xff1a;放行相关资源 & 保证启动了knife4j第四步&#xff1a…

Lecture 14:Life-long Learning

目录 Catastrophic Forgetting 灾难性遗忘(Catastrophic Forgetting)的克服之道 Selective Synaptic Plasticity Additional Neural Resource Allocation Memory Reply 其他 Catastrophic Forgetting ——为什么今日的人工智慧无法成为天网&#xff1f;灾难性遗忘 Life…

将ssh发布密钥添加到服务器的ssh授权密钥中,但是为什么我仍然无法ssh登录到此服务器?

我已经将ssh发布密钥添加到服务器的ssh授权密钥中&#xff0c;但是为什么我仍然无法ssh登录到此服务器&#xff1f; 即使将ssh公钥添加到服务器的授权密钥中&#xff0c;您也可能无法通过SSH登录到服务器&#xff0c;这有几个原因: 1.服务器的authorized_keys文件的权限不正确…

DAY 56 MySQL数据库的索引

索引的概念 索引是一个排序的列表&#xff0c;在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址&#xff08;类似于c语言的链表通过指针指向数据记录的内存地址&#xff09;。 使用索引后可以不用扫描全表来定位某行的数据&#xff0c;而是先通过索引表找到该行…