代码随想录day22 | 回溯算法理论基础 leetcode 77.组合 77.组合 加剪枝操作 216.组合总和III 17.电话号码的字母组合

news2024/12/19 15:18:23

DAY22 回溯算法开始 学到目前最烧脑的一天

回溯算法理论基础

任何回溯算法都可以抽象成一个树结构

image.png


理论基础

什么是回溯法

回溯法也可以叫做回溯搜索法,它是一种搜索的方式。
在二叉树系列中,我们已经不止一次,提到了回溯
回溯是递归的副产品,只要有递归就会有回溯。
所以以下讲解中,回溯函数也就是递归函数,指的都是一个函数

回溯法的效率

回溯法的性能如何呢,这里要和大家说清楚了,虽然回溯法很难,很不好理解,但是回溯法并不是什么高效的算法
因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。
那么既然回溯法并不高效为什么还要用它呢?
因为没得选,一些问题能暴力搜出来就不错了,撑死了再剪枝一下,还没有更高效的解法。
此时大家应该好奇了,都什么问题,这么牛逼,只能暴力搜索。

回溯法解决的问题

回溯法,一般可以解决如下几种问题:

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 棋盘问题:N皇后,解数独等等

相信大家看着这些之后会发现,每个问题,都不简单!
另外,会有一些同学可能分不清什么是组合,什么是排列?
组合是不强调元素顺序的,排列是强调元素顺序
例如:{1, 2} 和 {2, 1} 在组合上,就是一个集合,因为不强调顺序,而要是排列的话,{1, 2} 和 {2, 1} 就是两个集合了。
记住组合无序,排列有序,就可以了。

如何理解回溯法

回溯法解决的问题都可以抽象为树形结构,是的,我指的是所有回溯法的问题都可以抽象为树形结构!
因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度就构成了树的深度
递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。

回溯算法的基本思路:

  1. 选择:在当前状态下,尝试所有可能的选择。
  2. 约束:在做出选择后,检查当前选择是否满足问题的约束条件。
  3. 撤销:如果选择不满足条件或达到最终状态,则撤销该选择(回溯)。
  4. 终止条件:到达目标状态时,记录解或停止递归。

回溯算法框架 !!!

典型的伪代码如下:

void backtrack(参数) {
    if (满足结束条件) {
        保存结果;
        return;
    }
    

for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
    处理节点;
    backtracking(路径,选择列表); // 递归
    回溯,撤销处理结果 即与处理节点为相反操作
}

回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。
image.png
for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次。
backtracking这里自己调用自己,实现递归。
大家可以从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。

77.组合

Java:

未剪枝优化

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();  //因为要进行删除末尾的操作,(pop) 所以用LikedList
    public List<List<Integer>> combine(int n, int k) {
        backtracking(n, k, 1);
        return result;
    }
    public void backtracking(int n, int k, int startIndex) {
        if(path.size() == k) { // 递归终止条件:当前路径已经满足目标长度
            result.add(new ArrayList<> (path)); // 保存当前路径
            return;
        }
        for(int i = startIndex; i <= n; i++) { // 遍历当前范围的数字
            path.add(i); // 选择数字 i,加入到路径
            backtracking(n, k, i + 1); // 递归,选择下一个数字,因为要求是组合,不能有重复,所以范围从 i+1 开始 
            path.removeLast(); // 回溯,撤销当前选择
        }
    }
}

回溯思想解析

  1. 选择:
    • for 循环中,依次选择一个数字加入到当前路径 path 中。
    • 每次选择后,进入下一层递归,继续选择下一个数字。
  2. 递归:
    • 递归的过程就是尝试不同数字组合的过程。
    • 通过更新 startIndex,确保每次选择不会重复(如 [1, 2][2, 1] 只会生成一次)。
  3. 回溯:
    • 如果当前路径长度达到了目标长度(path.size() == k),将其保存到结果集后,回溯到上一步。
    • 回溯通过 path.removeLast() 撤销最后一次选择,尝试其他可能性。

77.组合 加剪枝操作

此剪枝是for里面的边界控制

java

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> combine(int n, int k) {
        backtracking(n, k, 1);
        return result;
    }
    public void backtracking(int n, int k, int startIndex) {
        if(path.size() == k) {
            result.add(new ArrayList<> (path));
            return;
        }
        for(int i = startIndex; i <=  n - (k - path.size()) + 1; i++) {
            path.add(i);
            backtracking(n, k, i + 1);
            path.removeLast();
        }
    }
}

n - (k - path.size()) + 1 的含义

背景

我们的问题是从 1n 中选择 k 个数字组成组合。path 是我们当前已经选择的数字,path.size() 代表当前选择的数字个数。

  • n 是总数字的范围(从 1 到 n)。
  • k 是我们需要选择的数字个数。
  • path.size() 是我们当前已经选择的数字个数。
  • k - path.size() 是我们还需要选择的数字个数。

剪枝的目标

我们希望在遍历时剪去那些不可能组成有效组合的分支。比如,如果剩下的数字不足以组成一个长度为 k 的组合,就停止递归,避免不必要的计算。

n - (k - path.size()) + 1 的含义

假设当前选择的数字已经有 path.size() 个,还需要选择 k - path.size() 个数字。此时,剩下的可选数字从当前位置 i 开始,最多有多少个数字可以用来构成组合呢?

1. 剩余需要选择的数字个数:
  • 当前已经选择的数字是 path.size()
  • 我们还需要选择 k - path.size() 个数字。

这意味着,从当前位置 i 开始,我们必须确保剩下的数字至少有 k - path.size() 个,才能继续构成有效的组合。

2. 剩余数字的总数:

假设当前位置是 i,那么从 i 开始,到 n 的数字有多少个呢?这个数字的数量是 n - i + 1(从 in 包括 i 本身)。

3. 限制条件:

为了保证我们能够从当前位置 i 开始选择,剩余的数字总数必须至少是我们需要选择的数字个数,即 k - path.size()
所以,剩余的数字个数应该满足:

n - i + 1 >= k - path.size()

调整顺序得

i <=  n - (k - path.size()) + 1

216.组合总和III

Java

class Solution {
    //因为只使用数字1到9,所以[1, n]n变成了9 边界控制就变成了9 - (k - path.size()) + 1
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        backtracking(n, k, 0, 1);   //n含义trageSum k含义path.size 0含义sum  1含义startIndex
        return result;
    }
    public void backtracking(int trageSum, int k, int sum, int startIndex) {
        if(sum > trageSum) return;
        if(path.size() == k){
            if(sum == trageSum) {
            result.add(new ArrayList<>(path));     
            }
        return;
        }
        for(int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
            sum += i;
            path.add(i);
            backtracking(trageSum, k, sum , i + 1);
            sum -= i;//可以不写 因为 Java 的基本类型是按值传递的,sum 在递归调用中已经发生了变化,不需要显式地恢复。
            path.removeLast();
        }
    }
}

这道题中剪枝操作除了边界处理i <= 9 - (k - path.size()) + 1还有if(sum > trageSum) return

17.电话号码的字母组合

Java

class Solution {

    //设置全局列表存储最后的结果
    List<String> list = new ArrayList<>();

    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) {
            return list;
        }
        //初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""
        String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        //迭代处理
        backTracking(digits, numString, 0);
        return list;

    }

    //每次迭代获取一个字符串,所以会涉及大量的字符串拼接,所以这里选择更为高效的 StringBuilder
    StringBuilder temp = new StringBuilder();

    //比如digits如果为"23",num 为0,则str表示2对应的 abc
    public void backTracking(String digits, String[] numString, int num) {
        //遍历全部一次记录一次得到的字符串
        if (num == digits.length()) {
            list.add(temp.toString());
            return;
        }
        //str 表示当前num对应的字符串
        String str = numString[digits.charAt(num) - '0'];
        for (int i = 0; i < str.length(); i++) {
            temp.append(str.charAt(i));
            //递归,处理下一层
            backTracking(digits, numString, num + 1);
            //剔除末尾的继续尝试
            temp.deleteCharAt(temp.length() - 1);
        }
    }
}
  • str.charAt(i):这个方法用于从字符串 str 中获取索引为 i 的字符。str 是当前数字对应的字母字符串(例如,如果当前处理的是数字 2,则 str = "abc")。charAt(i) 会返回该字符串中的第 i 个字符,比如 str.charAt(0) 会返回字符 'a'str.charAt(1) 会返回字符 'b',依此类推。
  • temp.append(...)appendStringBuilder 的方法,用于将参数(此处是字符)添加到 StringBuilder 对象 temp 的末尾。StringBuilder 是一个可变的字符串构建工具,可以高效地处理字符串的拼接操作。

总结:最烧脑的一集

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

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

相关文章

画一颗随机数

代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>codePen - Random Tree</title> </head> <body><canvas></canvas><script>const canvas doc…

牛客周赛 Round 72 题解

本次牛客最后一个线段树之前我也没碰到过&#xff0c;等后续复习到线段树再把那个题当例题发出来 小红的01串&#xff08;一&#xff09; 思路&#xff1a;正常模拟&#xff0c;从前往后遍历一遍去统计即可 #include<bits/stdc.h> using namespace std; #define int lo…

[x86 ubuntu22.04]投影模式选择“只使用外部”,外部edp屏幕无背光

1 问题描述 CPU&#xff1a;G6900E OS&#xff1a;ubuntu22.04 Kernel&#xff1a;6.8.0-49-generic 系统下有两个一样的 edp 屏幕&#xff0c;投影模式选择“只使用外部”&#xff0c;内部 edp 屏幕灭&#xff0c;外部 edp 屏幕无背光。DP-1 是外部 edp 屏幕&#xff0c;eDP-1…

清理C盘小记

突然C盘就爆满了&#xff0c;想当初还是给他预留了120G的空间&#xff0c;感觉到现在也不够用了&#xff0c;担心出现死机的情况就赶紧进行了清理。有一说一&#xff0c;清理回收站是真的有用。 参考&#xff1a;C盘清理指南&#xff0c;清理出30G起&#xff0c;超详细总结&am…

Docker:Docker Compose(补充三)

Docker&#xff1a;Docker Compose 1. Docker Compose 批量管理容器的工具 1. Docker Compose 批量管理容器的工具 Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过一个 YAML 文件来配置应用服务&#xff0c;它允许用户编排、组合和配置多个容器的部署…

lightRAG 论文阅读笔记

论文原文 https://arxiv.org/pdf/2410.05779v1 这里我先说一下自己的感受&#xff0c;这篇论文整体看下来&#xff0c;没有太多惊艳的地方。核心就是利用知识图谱&#xff0c;通过模型对文档抽取实体和关系。 然后基于此来构建查询。核心问题还是在解决知识之间的连接问题。 论…

Visual studio的AI插件-通义灵码

通义灵码 TONGYI Lingma 兼容 Visual Studio、Visual Studio Code、JetBrains IDEs 等主流 IDE&#xff1b;支持 Java、Python、Go、C/C、C#、JavaScript、TypeScript、PHP、Ruby、Rust、Scala 等主流编程语言。 安装 打开扩展管理器&#xff0c;搜送“TONGYI Lingma”&…

shutil 文件拷贝copy - python 实现

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…

attack xv6

思路 被这个实验折磨了两天&#xff0c;可能是2024新出的一个实验内容&#xff0c;网上资料少&#xff0c;参考了一篇仅有的博客&#xff0c;吭哧吭哧分析出来了个大概吧…在此记录一下&#xff0c;以便帮助有需要的人。 attack xv6的ans只有几行代码&#xff0c;根据实验描述…

Flink CDC实时同步mysql数据

官方参考资料&#xff1a; https://nightlies.apache.org/flink/flink-cdc-docs-master/zh/docs/connectors/flink-sources/mysql-cdc/ Apache Flink 的 Change Data Capture (CDC) 是一种用于捕获数据库变化&#xff08;如插入、更新和删除操作&#xff09;的技术。Flink CDC…

eclipse 如何设置项目、不同类型文件的 utf8 编码

编码问题一直是软件开发中让人头疼的小细节&#xff0c;尤其是团队协作中&#xff0c;若编码格式不统一&#xff0c;乱码问题便会频繁出现。那么如何在 Eclipse 中统一设置项目和文件的 UTF-8 编码&#xff0c;避免因编码问题造成不必要的困扰呢&#xff1f;今天&#xff0c;我…

Unity中触发器Trigger无法被射线检测到的问题

今天在做项目的时候发现,同一个物体,当他是碰撞器的时候,可以被射线检测到. 但是当他变成触发器的时候,射线就检测不到了??? 本来以为就是这样的,但是查了资料发现并没有这样的限制,触发器也是可以正常被射线检测的 到处查资料都没有发现问题,后来发现是下面这个设置不知道…

第一个AJAX调用XMLHttpRequest

第一个AJAX调用XMLHttpRequest 创建对象&#xff0c;用于浏览器和服务器的通信&#xff0c;不需要刷新浏览器 const request new XMLHttpRequest();通过GET请求方式在API中请求数据 request.open(GET, https://restcountries.com/v3.1/name/Russia);注&#xff1a;我这里的…

群落生态学研究进展】Hmsc包开展单物种和多物种分析的技术细节及Hmsc包的实际应用

联合物种分布模型&#xff08;Joint Species Distribution Modelling&#xff0c;JSDM&#xff09;在生态学领域&#xff0c;特别是群落生态学中发展最为迅速&#xff0c;它在分析和解读群落生态数据的革命性和独特视角使其受到广大国内外学者的关注。在学界不同研究团队研发出…

如何在 Apifox 中发布多语言的 API 文档?

“API 文档是开发协作的桥梁&#xff0c;而多语言支持则让这座桥梁跨越更多的技术边界。使用 Apifox&#xff0c;不仅可以快速生成 API 文档&#xff0c;还能轻松实现多语言的支持与发布。今天&#xff0c;我们一起来探索 Apifox 在多语言文档发布中的最佳实践&#xff01;” …

华为云检查服务器状态

VNC方式登录云服务器正常&#xff0c;但无法通过远程桌面连接方式登录云服务器时&#xff0c;推荐您按照以下思路排查问题。 以下排查思路根据原因的出现概率进行排序&#xff0c;建议您从高频率原因往低频率原因排查&#xff0c;从而帮助您快速找到问题的原因。 如果解决完某…

leetcode212. 单词搜索 II

给定一个 m x n 二维字符网格 board 和一个单词&#xff08;字符串&#xff09;列表 words&#xff0c; 返回所有二维网格上的单词 。 单词必须按照字母顺序&#xff0c;通过 相邻的单元格 内的字母构成&#xff0c;其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一…

无缝钢管内表面缺陷检测的自强化感知协调网络

摘要 无缝钢管是重要的工业材料。然而&#xff0c;无缝钢管中的内表面缺陷检测具有挑战性&#xff0c;并且会显著影响无缝钢管的性能和寿命。现有的检测方法是劳动强度大的&#xff0c;并且检测结果的可视化程度低。因此&#xff0c;本文提出了一种新型的管道内表面缺陷螺旋式全…

Kioptrix靶场渗透--level1.1

目录 环境搭建 镜像包下载 下载后解压&#xff0c;并修改配置 开始渗透 获取IP 获取具体服务 尝试sql注入 尝试命令执行 反弹shell 提权 查看Linux的内核版本 下载脚本 将脚本上传至靶机 编译脚本 编译后执行 第一个脚本失败&#xff0c;再找个脚本 使用cat /…