LeetCode刷题日记之回溯算法(一)

news2024/11/24 17:40:36

目录

  • 前言
  • 组合
  • 组合总和III
  • 电话号码的字母组合
  • 总结


前言

今天开始学习回溯算法啦,虽然直接递归学习的时候有涉及到回溯但是没有系统性的学习,希望博主记录的内容能够对大家有所帮助 ,一起加油吧朋友们!💪💪💪


组合

LeetCode题目链接

给定一个整数n和一个整数k,要求返回[1, n]中所有可能的k个数的组合
请添加图片描述
首先的话需要知道组合的特点是顺序不重要,这启示我们可以用列表来存🤔🤔🤔

我们来梳理逻辑

  • 回溯法的核心是用递归来构建每一个可能的组合,通过递归遍历所有可能的数并在选择时跳过已经选择过的数字🤔,所以我们可以定义一个结果列表result,一个组合列表path,一个数字索引startIndex,每层往下数字索引递增,选择数字加入path,当path的长度等于k则把组合加入结果集,递归完成后回溯path来存储其他组合😲

我们来进一步梳理回溯三要素

  • 递归函数的参数和返回值
//把结果集和组合定义为全局变量,避免太多参数导致递归处理不好理解
List<List<Integer>> result = new ArrayList<>();//存放符合条件结果的集合 List<Integer> path = new ArrayList<>();//存放符合条件的结果

//递归处理无返回值,递归完result已经填充完毕,n为数的最大值,k为组合大小,在递归处理中需要
private void backtracking(int n, int k, int startIndex){}
  • 回溯函数终止条件(这里要生成path副本来存入结果集中,如果存path的话只是一个引用,path回溯撤销时,result中的元素也将同时撤销🤔🤔🤔)
if(path.size() == k){ //终止条件:组合长度等于k
    result.add(new ArrayList<>(path));//存放结果
    return;
}
  • 单层搜索过程(这里是包含未剪枝处理的搜索逻辑与剪枝的搜索逻辑,因为有些不必要的搜索可以剪掉🤔🤔🤔,剪掉这些搜索就称为剪枝,这里剪枝是因为要组合长度为k,然后如果数字索引到后面总的搜索次数都不够添加k个元素形成一个组合那就没有必要搜索了🤔🤔🤔)
for(int i = startIndex; i <= n; i++){//选择本层集合中的元素
    path.add(i);//处理节点
    backtracking(n, k, i + 1);//递归
    path.removeLast();//回溯撤销处理的节点
}
for(int i = startIndex; i <= n - (k - path.size()) + 1; i++){//选择本层集合中的元素
    path.add(i);//处理节点
    backtracking(n, k, i + 1);//递归
    path.removeLast();//回溯撤销处理的节点
}

回溯的完整代码如下

//未剪枝
// class Solution {
//     List<List<Integer>> result = new ArrayList<>();//存放符合条件结果的集合
//     List<Integer> path = new ArrayList<>();//存放符合条件的结果
//     public List<List<Integer>> combine(int n, int k) {
//         backtracking(n, k, 1);
//         return result;
//     }

//     private 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);//处理节点
        //     backtracking(n, k, i + 1);//递归
        //     path.removeLast();//回溯撤销处理的节点
        // }
//     }
// }

//剪枝
class Solution {
    List<List<Integer>> result = new ArrayList<>();//存放符合条件结果的集合
    List<Integer> path = new ArrayList<>();//存放符合条件的结果
    public List<List<Integer>> combine(int n, int k) {
        backtracking(n, k, 1);
        return result;
    }

    private void backtracking(int n, int k, int startIndex){ //可剪枝处
        if(path.size() == k){ //终止条件
            result.add(new ArrayList<>(path));//存放结果
            return;
        }
        /**
        如果for循环选择的起始位置之后的元素个数已经不足我们需要的元素个数那就没有必要搜索了
        已经选择的元素path.size()
        还需要选择的元素k - path.size()
        在集合中至多要从n - (k - path.size()) + 1开始遍历
         */
        for(int i = startIndex; i <= n - (k - path.size()) + 1; i++){//选择本层集合中的元素
            path.add(i);//处理节点
            backtracking(n, k, i + 1);//递归
            path.removeLast();//回溯撤销处理的节点
        }
    }
}

组合总和III

LeetCode题目链接

就是也是找组合,找什么组合呢?从[1,9]区间里找一个长度为k的组合,使得这个组合的数总和为n,组合中每个数只能出现一次
请添加图片描述
我们来梳理一下逻辑

  • 回溯搜索的话从区间1~9取数,向组合中添加数字,累计当前的总和。 在递归过程中,如果组合的长度达到了 k 且组合的和等于 n,则该组合为有效结果,加入到结果集中。如果组合长度超过 k,或者当前数字的和已经超过 n,则不需要继续递归,进行回溯。每次递归时尝试选择数字,之后在递归返回时将该数字移除(即回溯),以便尝试其他可能性🤔🤔🤔

我们来进一步梳理回溯三要素

  • 递归函数的参数和返回值
//定义结果集和组合的全局变量,减少递归参数
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
//回溯搜索所需的数字索引、目标和n、组合长度k
private void backtracking(int k, int n, int startIndex){}
  • 回溯终止条件
if(path.size() == k){//终止条件
    if(path.stream().mapToInt(Integer::intValue).sum() == n) result.add(new ArrayList<>(path));//如果组合的和为目标和且组合长度为目标长度则把组合副本加入结果集
    return;
}
  • 单层搜索过程(这里大家选一种写即可,这里剪枝的话包含循环时把不够组合长度的搜索剪掉以及把组合的和大于目标和后的不必要递归也剪掉🤔🤔🤔)
//未剪枝的单层搜索过程
for(int i = startIndex; i <= 9; i++){ 
    path.add(i);
    backtracking(k, n, i + 1);
    path.removeLast();//回溯
}
//剪枝的单层搜索过程
for(int i = startIndex; i <= 9 - (k - path.size()) + 1 ; i++){//个数剪枝
    path.add(i);
    if(path.stream().mapToInt(Integer::intValue).sum() > n){//求和剪枝
        path.removeLast();//先回溯
        return;
    }
    backtracking(k, n, i + 1);
    path.removeLast();//回溯
}

回溯的完整代码如下

/**不剪枝 */
// class Solution {
//     List<List<Integer>> result = new ArrayList<>();
//     List<Integer> path = new ArrayList<>();
//     public List<List<Integer>> combinationSum3(int k, int n) {
//         backtracking(k, n, 1);
//         return result;
//     }
//     private void backtracking(int k, int n, int startIndex){
//         if(path.size() == k){//终止条件
//             if(path.stream().mapToInt(Integer::intValue).sum() == n) result.add(new ArrayList<>(path));
//             return;
//         }
        // for(int i = startIndex; i <= 9; i++){ 
        //     path.add(i);
        //     backtracking(k, n, i + 1);
        //     path.removeLast();//回溯
        // }
//     }
    
// }

/**剪枝 */
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        backtracking(k, n, 1);
        return result;
    }
    private void backtracking(int k, int n, int startIndex){
        if(path.size() == k){//终止条件
            if(path.stream().mapToInt(Integer::intValue).sum() == n) result.add(new ArrayList<>(path));
            return;
        }
        for(int i = startIndex; i <= 9 - (k - path.size()) + 1 ; i++){//个数剪枝
            path.add(i);
            if(path.stream().mapToInt(Integer::intValue).sum() > n){//求和剪枝
                path.removeLast();//先回溯
                return;
            }
            backtracking(k, n, i + 1);
            path.removeLast();//回溯
        }
    }
    
}

电话号码的字母组合

LeetCode题目链接

这道题就是给一个字符串类似"23",然后的话手机按键上不是像2或者3它会各自对应一组字符类似"abc"这种,然后就是把对应的字符的所有组合进行一个返回。
请添加图片描述
我们来梳理思路

  • 先定义一个数字到字母的映射表(类似于电话键盘上的数字对应字母),然后使用回溯法从第一个数字开始,每次选择该数字对应的一个字母,递归进入下一个数字,继续选择字母。 当递归路径达到输入字符串的长度时,说明已经生成了一个完整的字母组合,将它加入结果集中。 在回溯过程中每次递归完成后回到上一步,尝试其他字母组合,直至所有组合都被遍历🤔🤔🤔

我们进一步来梳理回溯三要素

  • 确定递归的参数和返回值
List<String> result = new ArrayList<>();
StringBuilder s = new StringBuilder();//会涉及大量的字符串拼接,所以这里选择更为高效的 StringBuilder
String[] map = {
    "",
    "",
    "abc",
    "def",
    "ghi",
    "jkl",
    "mno",
    "pqrs",
    "tuv",
    "wxyz"
};
private void backtracking(String digits, int index){}
  • 回溯终止条件(数字索引等于给的数字字符串长度时把字母组合加入到结果集中🤔)
if(index == digits.length()){ //出口
    result.add(s.toString());
    return;
}
  • 单层搜索过程
String str = map[digits.charAt(index) - '0'];//先根据数字索引找对应的可用字母
for(int i = 0; i < str.length(); i++){//递归进行字母组合
    s.append(str.charAt(i));
    backtracking(digits, index + 1);
    s.deleteCharAt(s.length() - 1);
}

完整的回溯代码如下:

class Solution {
    List<String> result = new ArrayList<>();
    StringBuilder s = new StringBuilder();//会涉及大量的字符串拼接,所以这里选择更为高效的 StringBuilder
    String[] map = {
        "",
        "",
        "abc",
        "def",
        "ghi",
        "jkl",
        "mno",
        "pqrs",
        "tuv",
        "wxyz"
    };
    public List<String> letterCombinations(String digits) {
        if(digits.length() == 0)return result;
        backtracking(digits, 0);
        return result;
    }

    private void backtracking(String digits, int index){ //index是用来遍历digits的
        if(index == digits.length()){ //出口
            result.add(s.toString());
            return;
        }
        String str = map[digits.charAt(index) - '0'];
        for(int i = 0; i < str.length(); i++){
            s.append(str.charAt(i));
            backtracking(digits, index + 1);
            s.deleteCharAt(s.length() - 1);
        }
    }
}

总结

今天的回溯学习就到这里啦,继续加油,奥利给✊✊✊

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

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

相关文章

飞腾X100适配Ubuntu说明

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…

实践体验密集小目标检测,以小麦麦穗颗粒为基准,基于嵌入式端超轻量级模型LeYOLO全系列【n/s/m/l】参数模型开发构建智能精准麦穗颗粒检测计数系统

对于常规的目标检测任务来说&#xff0c;诸如&#xff1a;COCO、VOC这类基础的数据场景&#xff0c;涌现出来了一些列性能初衷的检测模型&#xff0c;YOLO系列就是其中的佼佼者&#xff0c;不断地刷榜取得了越来越好的效果&#xff0c;不过这些评测指标是基于COCO、VOC这类公开…

基于Python+sqlite3实现(Web)图书管理系统

项目名称&#xff1a;LibraryManagementSystem 一、系统目标 使用了Python作为语言,以django为后台&#xff0c;sqlite3作为数据库&#xff0c;UI基于bootstrap的图书管理系统&#xff0c;模拟图书管理的真实场景&#xff0c;考虑客观需求&#xff0c;界面简洁、操作方便&…

解决Kali直接使用root用户密码ssh远程登录不上问题

一、问题描述 当我们直接使用root用户和密码ssh远程登录时&#xff08;ssh rootKali主机的IP地址&#xff09;直接提示“SSH服务器拒绝了密码&#xff0c;请再试一次”或者“Permission denied, please try again.&#xff08;权限被拒绝&#xff0c;请重试&#xff09;”信息&…

腾讯云视立方Electron 相关问题

安装相关 trtc-electron-sdk 是否兼容官方 Electron v12.0.1 版本? 兼容的&#xff0c;trtc-electron-sdk 没有特别依赖 elecron 自身的 SDK&#xff0c;所以没有相关的版本依赖。 Electron 下载慢甚至卡住不动&#xff1f; 当开始下载tmp-3320-1-SHASUMS256.txt-6.1.9文件…

考研C语言程序设计_语法相关习题(持续更新)

目录 一、语法题strlen转义字符内置数据类型字符串结束标志局部变量和全局变量名字冲突 局部优先switch语句中的关键字数组初始化是否正确注意define不是关键字C语言中不能用连等判断switch( )的括号里可以是什么类型? 二、程序阅读题有关static有关continue说明代码的功能for…

Postgresql JSON操作符

postgresql json数据 参考文章&#xff1a; JSON数据操作 操作符 1、-> 表示获取一个JSON数组元素&#xff0c;支持下标值(下标从0开始)、Key获取。 2、->> 表示获取一个JSON对象字符串。 3、#> 表示获取指定路径的一个JSON对象。 4、#>>表示获取指定路径…

脉冲目标检测网络模型SpikeYOLO——基于整值训练和脉冲驱动推理的高性能节能目标检测神经网络

最近看到目标检测领域又出新作&#xff0c;ECCV2024 满分Oral论文——《Integer-Valued Training and Spike-Driven Inference Spiking Neural Network for High-performance and Energy-efficient Object Detection》论文地址在这里&#xff0c;如下所示&#xff1a; 感兴趣的…

浪潮信息领航边缘计算,推动AI与各行业深度融合

在9月20日于安徽盛大召开的浪潮信息边缘计算合作伙伴大会上&#xff0c;浪潮信息指出&#xff0c;未来的计算领域将全面融入AI技术&#xff0c;特别是在企业边缘侧&#xff0c;智能应用特别是生成式人工智能应用正在迅速普及&#xff0c;这一趋势正引领边缘计算向边缘智算的方向…

R语言机器学习算法实战系列(三)lightGBM算法(Light Gradient Boosting Machine)

文章目录 介绍原理:应用方向:教程下载数据加载R包导入数据数据预处理数据描述数据切割设置数据对象调节参数训练模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve特征的重要性模型SHAP值解释保存模型总结系统信息介绍 LightGBM(Light Gradient B…

QEMU与KVM架构

完整架构图&#xff0c;来自QEMU官网 QEMU与KVM架构总体上分为3部分。 VMX root模式的应用层&#xff08;左上&#xff09; VMX root模式的内核层&#xff08;左下&#xff09; 虚拟机的运行&#xff08;右上&#xff09; VMX root相对于VMX non-root模式&#xff0c;CPU引入了…

基于Arduino的自动浇灌系统

基于Arduino的自动浇灌系统 一、项目说明二、项目材料三、电路设计四、怎样工作五、怎样设置 一、项目说明 嘿&#xff0c;伙计们&#xff0c;在这篇教程中&#xff0c;我将向你们展示如何使用Arduino制作一个植物浇水系统&#xff0c;以一种简单的方式帮助你始终保持植物的水…

【MySQL】mysql导出数据WPS科学计数法解决方法

导出的长串数字 id 会导致科学计数法&#xff0c;修改 WPS 单元格格式可以解决 数字太长还是有问题&#xff0c;最后有个数字会变成 0 可以 直接用 python脚本转换一下 vim convert_txt_xlsx.py #!/usr/bin/env python3# 使用方法# 安装库 # pip3 install pandas openpyxl…

见微知著:OpenEuler系统启动流程

OpenEuler是一个开源的Linux发行版&#xff0c;它的启动流程涉及到多个阶段&#xff0c;包括固件初始化、引导加载程序、内核启动、初始化系统和服务管理器等。下面将详细介绍OpenEuler的启动流程。 一、启动流程 1. 固件初始化&#xff08;BIOS/UEFI&#xff09; 启动过程首…

跟踪用户状态,http协议无状态 Cookie HttpSession,Session和Cookie的关系

1.概念分析 跟踪用户状态指的是web应用能够分辨请求属于哪个用户&#xff0c;进而记录用户的状态&#xff0c;从而为用户提供连续的针对性的服务。比如有多个客户在同一个购物网站上购物&#xff0c;每一个用户都会有一个虚拟的购物车。当某个客户发送请求将商品添加到购物车时…

部署 Docker harbor (httphttps)及使用

部署 Docker harbor (http/https)及使用 官网下载docker harbor 和docker-compose 下载最新版本即可 https://github.com/goharbor/harbor https://github.com/docker/compose/releases 一.Docker harbor 配置http使用 1.解压harbor.tar # 解压至指定目录 [rootdocker ~]…

线程池 jvm web

线程池 分类 newCachedThreadPool 可进行缓存重复利用的线程池 newFxiedThreadPool 可重复利用的固定数目的线程池 newSingelThreadPool 单个work线程 newSingelThreadScheduledExecutor 单线程定时执行程序 newWorkStealingPool 带并行级别的线程池 核心参数 …

【OpenCV】(二)—— 图片读取展示和保存

上一小节中我们成功安装了opencv&#xff0c;我们这次学习使用opencv最基础的功能&#xff0c;读取和展示图片&#xff0c;首先准备一张用于实验的样例图片【cat.jpg】如下&#xff1a; 然后就是创建一个python项目并导入相关依赖 import cv2读取图片 读取图片使用imread方法…

Flythings学习(二)控件相关

文章目录 1 前言2 通用属性2.1 控件ID值2.2 控件位置2.3 背景色2.4 背景图2.5 显示与隐藏2.6 控件状态2.7 蜂鸣器控制 3 文本类TextView4 按键类 Button4.1 系统按键4.2 处理按钮长按事件4.3 处理按键触摸事件 5 复选框CheckBox6 单选组 RadioGroup7 进度条&#xff0c;滑块7.1…

vscode如何通过ssh远程链接其它电脑

客户端&#xff08;本机&#xff09;linux系统中vscode通过ssh插件远程连接服务器&#xff0c;其操作步骤如下&#xff1a; 配置服务器的配置文件 首先在~/.ssh/config文件中输入服务器的配置信息&#xff0c;如果没有这个文件就新建一个&#xff0c;其内容如下&#xff1a; …