回溯算法总结

news2024/11/17 12:29:54

目录

  • 介绍
  • 伪代码
    • 例题:
    • 解释:
  • 回溯算法中的优化
    • 去重
      • 伪代码
    • 剪枝
  • 常见题型
    • 子集例题
    • 全排列例题
  • 参考资料

介绍

  递归(DFS)是一个劲的往某一个方向搜索,而回溯算法建立在 DFS 基础之上的,但不同的是在搜索过程中,达到结束条件后,恢复状态,回溯上一层,再次搜索。因此回溯算法与 DFS 的区别就是有无状态重置。

伪代码

    List<List<T>> res = new ArrayList<>(); //记录答案
    List<T> path = new ArrayList<>();  //记录路径
    public void backtracking(参数列表){
        //  可以有,也可不写,毕竟我们for循环也是有
        if(终止条件){
            //收集结果
            //res.add();
            return;
        }
        
        for(元素集){
            // 处理结点
            
            // 递归
            
            // 回溯操作 ——> 撤销处理结点的情况
            // 所以我们是否存在回溯,都取决于我们是否多次处理了结点,他们两个要匹配
            
        }
    }


例题:

​ 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

​ candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

请添加图片描述

解释:

​ 简单来说,就是递归,回溯是一个暴力手段,找得到就存,找不到的时候一旦到达叶子底部就回退路径,换一条路,其核心就是他的模板内容

	List<List<Integer>> result = new ArrayList(); // 保存结果
	/*
	 保存路径 ->但是保存的时候要按着这个path重新new一个,引用对象,我们不改变对象
	 地址也就不变,但是改变这个对象的值,并不会影响对象地址,事实上我们时刻在改变
	 这个path集合元素,所以当元素条件符合时我们new一个出来让他保存这个结果,放到result中
	 让path继续运动
	*/
	List<Integer> path = new ArrayList();		
	public void backtracking(int[] temp,int target,int sum,int start ){
		// 结束条件
        if(sum == target){
            res.add(new ArrayList(path));
            return;
        }
        
        for(int i = 0 ; i < temp.length ; i++){
            int tt = temp[i] + sum ; 
            if(tt < target){
                // 处理结点
                path.add(temp[i]);
                //递归
                backtracking(temp.target,tt,i);
                // 回溯操作 ——> 撤销处理结点的情况
                path.remove(path.size()-1);
            }else break;
        }
    }
	public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtracking(candidates,0,target,0);
        return res;
    }

回溯算法中的优化

去重

​ 我们一般取得数据都是取得叶子结点上的值,所以只要我们每次保证,在有序的集合中,相同层级下取出的数据和上一次回溯的结果不同即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aHunEUW9-1671289185260)(算法总结/image-20220902210359548.png)]

以这个为例,我们第一次可以直接取到地,但是这个时候我们也获取的第一次回溯的结点 :2,我们下次就不能再去取他,这样 1、1、x,x只能取2,3,4这样就保证在1、1开头的组合没有重复的组合,同理当层取完回溯之后我们1、1开头的数据就取完了,第二个结点就不能再去取1了同理,其它层也是如此理解。

伪代码

public static List<List<Integer>> res = new ArrayList<>(); // 所有的叶子结点数据
public static List<Integer> path = new ArrayList<>(); // 当前路径的值,其实是一个临时变量

public /* 你要返回的数据类型 */ solution(int[] nums){
    Arrays.sort(nums);//在去重之前我们要保证有序
    dsf(nums,0);
    
}

public void dsf(int[] nums ,int start ){
    
    if(/*满足要求,或者到达界限开始判断存储*/){
        // 自定义判断
        
        res.add(new ArrayList(path));
        return;
    }
    
    int temp = Integer.MAX_VALUE; // 用来记录回溯数据
    for (int i = start; i < nums.length; i++) {
        if (nums[i] == temp) { // 同级去下一个元素之前,不可以重复去取已经判断过的点了
            continue;
        }
        path.add(nums[i]);

        dsf(nums,i+1);

        temp = path.remove(path.size() - 1);
    }
}

剪枝

​ 其实剪枝就是提前退出循环、或者递归,这里要自己按照题目要求增加 if语句,比如在一个有序集合中我们要的数据必须小于0,但是我们在下一个数据就取到了5,而且所以之后的数据也就不用在进行递归遍历了,没有意义。

​ 大概就是这意思,剪枝一定要注意。他确实是一个优化的大手段。但是也要注意不要过分剪枝,导致数据缺失,这里就要求我们的 if语句判断必须严格,符合题意 。

常见题型

  • 求子集
  • 全排列

子集例题

Leetcode78题

	/** 
	* 最终结果的保存
    */
	private static List<List<Integer>> result = new ArrayList<>();
	/** 解题思路
    我们常在进行数据添加的时机都会选择在,我们的终止条件,也就是最开始的 if () ... 中
    但是这样会导致我们,在回溯的时候少了一些点,例如 1 3 4 在 1 3 4 选择之后 我们下一个结果是 1 4
    因为我们在 1 3 4 确定之后 别忘了我们其实是在3的基础上判断,所以 i+1处理之后我们就回溯,自然就将3删除了
    显然我们忽略了 1 3 这个数据。
     所以我们现在不拖到最后,在我们纵深的同时,也添加数据。这样我们不会漏掉一个数据,
     */
    public static void dsf(int[] nums, int start,List<Integer> path) {
        // result.add(new ArrayList<>(path));
        // if (!path.isEmpty()) result.add(new ArrayList<>(path)); //如果需要的是非空子集
        if (!path.isEmpty() && path.size() != nums.length) result.add(new ArrayList<>(path)); //如果需要的是非空真子集}
        for (int i = start; i < nums.length; ++i) {
            path.add(nums[i]);
            dsf(nums, i + 1, path);
            path.remove(path.size() - 1);
        }
    }
     public List<List<Integer>> subsets(int[] nums) {
			dsf(nums,0,new ArrayList());
			  return result;
    }
 
    /** 解题思路
    同上,我们要是还是想着直接纵深,临时保存就行,直到我们当前状态无法选择,再将我们得到的路径保存下来
    问题我们也说过了,我们通用的模板有一个问题就是漏数据了,我们在结束点的数据会直接回退两次,会让我们少数据
    所以我们只需要补偿这时候的数据即可
    
     缺点:无法直接获取全部的子集,因为空集我们不能直接获取
     */
    public void dfs(int cur, int[] nums,List<Integer> path) {
        if (cur == nums.length) {
            result.add(new ArrayList<Integer>(path));
            return;
        }
        path.add(nums[cur]);
        dfs(cur + 1, nums, path);
        path.remove(path.size() - 1);
        dfs(cur + 1, nums, path); // 补偿回退过程中即将缺少的数据
         /** 解释
         例如 原本集合 {1 3 4}
         我们已经到了 1 3 4,到第一次回溯完成,我们得到了他的第一个子集,同时我们去除了 4
         如果我们直接结束 我们 path={1,3} 状态下的3也会被删除,直接进入{1,4},
         因为我们是在{1,3}状态下,进入的下一状态,即 dsf ,我们处理完成,必然要回溯删除状态3
         所以我们在我们删除状态 4 的时候再次dsf,其实是我们对 3的补偿。
         */
    }   

全排列例题

伪代码

	/** 解题思路
    前提: 不考虑去重问题
    全排列问题就是 我们每个结点都可能在当前位置,所以我们每次都需要重头遍历,
    但是当前元素有且仅有一次出现,我们不能重复取这个元素,这样导致元素全部重复

    所以相对于取集合问题,我们需要知道我们下一个的位置,这里我们则需要知道那些元素取过了即可.
    如果后期遇到重复元素我们,我们也有专门去重的技巧,我们只需要记录上一次回溯的元素,在下一次
    添加操作缓存的时候进行一下判断即可。具体翻一下上面去重的内容
     */
    public static void dsf(int[] nums, boolean[] map, List<Integer> path) {
        if (path.size() == nums.length) {
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; ++i) {
            if (!map[i]) {
                path.add(nums[i]);
                map[i] = true;
                dsf(nums,  map, path);

                map[i] = false;
                path.remove(path.size() - 1);
            }
        }
    }

参考资料

  • LeetCode大神解答
  • 代码随想录中的老师,大哥哥讲的真的通俗简单,推荐先看这个,入门之后再看衍生例题,才有心思看下去
  • LeetCode官方

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

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

相关文章

SAP ABAP 扫描 ABAP 源代码(RS_ABAP_SOURCE_SCAN)

SAP ABAP 扫描 ABAP 源代码&#xff08;RS_ABAP_SOURCE_SCAN&#xff09; 引言&#xff1a; RS_ABAP_SOURCE_SCAN 是一个 SAP 标准 ABAP 报表程序&#xff0c;通过输入字符串文本&#xff0c;扫描 ABAP 源代码&#xff0c;列示查找字符串在 ABAP 源代码中出现的位置清单。本文…

20行python代码的入门级小游戏

0、背景&#xff1a; 作为一个python小白&#xff0c;今天从菜鸟教程上看了一些python的教程&#xff0c;看到了python的一些语法&#xff0c;对比起来&#xff08;有其他语言功底&#xff09;&#xff0c;感觉还是非常有趣&#xff0c;就随手添了一点内容&#xff0c;改了一个…

【车载开发系列】CAN总线通信---总线报文格式

【车载开发系列】CAN总线通信—总线报文格式 CAN总线通信---总线报文格式【车载开发系列】CAN总线通信---总线报文格式一.什么是ISO15765二.ISO15765的目的三.单帧传输的概念四.多帧传输的概念五.诊断报文格式1&#xff09;首帧FF2&#xff09;连续帧CF3&#xff09;流控帧FC4&…

【JavaScript】for循环

文章目录for循环案例1&#xff1a;两数相加案例2&#xff1a;绘制九九乘法表案例3&#xff1a;水仙花数案例4&#xff1a;绘制菱形案例5&#xff1a;计算表达式结果break和continue图片切换效果案例&#xff08;轮播图结构&#xff09;一、JavaScript代码二、HTML结构和CSS样式…

基于java+springmvc+mybatis+vue+mysql的演出道具租赁管理系统

项目介绍 前端页面&#xff1a; 功能&#xff1a;首页、道具出租、公告资讯、个人中心、后台管理 管理员后台页面&#xff1a; 功能&#xff1a;首页、个人中心、用户管理、商家管理、道具类型管理、道具出租管理、租赁订单管理、道具归还管理、我的收藏管理、系统管理 用户…

密西根大学张阳实验室郑伟博士在CASP15蛋白质结构预测大赛中斩获多项冠军

简报&#xff1a;在有着蛋白质结构预测领域奥林匹克竞赛之称的最新一届CASP比赛中&#xff08;CASP15&#xff09;&#xff0c;密西根大学张阳教授和Peter Freddolino教授实验室的郑伟博士在多个比赛项目中获得冠军。其中D-I-TASSER算法&#xff08;参赛名&#xff1a;“UM-TBM…

nodejs银行取号系统vue

目 录 1绪论 1 1.1项目研究的背景 1 1.2开发意义 1 1.3项目研究现状及内容 5 1.4论文结构 5 2开发技术介绍 7 2.1 B/S架构 7 2.2 MySQL 介绍 7 2.3 MySQL环境配置 7 3系统分析 9 3.1可行性分析 9 3.1.1技术可行性 9 3.1.2经济可行性 …

学会4种方法,掌握端到端测试处理数据..

推荐阅读&#xff1a; [内部资源] 想拿年薪30W的软件测试人员&#xff0c;这份资料必须领取~ Python自动化测试全栈性能测试全栈&#xff0c;挑战年薪40W 对Web应用程序运行自动化的端到端测试时&#xff0c;最常见的问题之一是如何处理测试数据。端到端测试通常会在通过应用…

授权服务器搭建以及授权码模式

前面的 GitHub 授权登录主要向大家展示了 OAuth2 中客户端的工作模式。对于大部分的开发者而言&#xff0c;日常接触到的 OAuth2 都是开发客户端&#xff0c;例如接入 QQ 登录、接入微信登录等。不过也有少量场景&#xff0c;可能需要开发者提供授权服务器与资源服务器&#xf…

Spring MVC【创建与使用】

Spring MVC【创建与使用】&#x1f34e;一.Spring MVC介绍&#x1f352;1.1 什么是SpringMVC?&#x1f352;1.2 MVC 定义&#x1f352;1.3 Spring MVC 与 MVC 的区别&#x1f352;1.4 Spring MVC的基本功能&#x1f34e;二. Spring MVC项目的创建&#x1f352;2.1 Spring MVC …

代码随想录DAY51 | 309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费

文章目录309.最佳买卖股票时机含冷冻期714.买卖股票的最佳时机含手续费309.最佳买卖股票时机含冷冻期 文章讲解&#xff1a;代码随想录 (programmercarl.com) 题目链接&#xff1a;309. 最佳买卖股票时机含冷冻期 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; …

Efficientdet源码详解

1.参数配置 最重要的参数配置如下 -p:配置文件&#xff0c;需要在project文件夹下新建配置文件-c:efficientdet的版本,efficientdet B0-B7-n:windows为0&#xff0c;linix根据自己的需求修改--batch_size:batch大小--data_path:数据集路径-p data --batch_size 16 其中&…

【Flask框架】——20 请求钩子

在客户端和服务器交互的过程中&#xff0c;有些准备工作或扫尾工作需要处理&#xff0c;比如&#xff1a; 在请求开始时&#xff0c;建立数据库连接&#xff1b; 在请求开始时&#xff0c;根据需求进行权限校验&#xff1b; 在请求结束时&#xff0c;指定数据的交互格式。 …

Dubbo 1 分布式系统中的相关概念 1.1 大型互联网项目结构目标

Dubbo 【黑马程序员Dubbo快速入门&#xff0c;Java分布式框架dubbo教程】 【非常重要就完事儿 了】 1 分布式系统中的相关概念 文章目录Dubbo1 分布式系统中的相关概念1.1 大型互联网项目结构目标1.1.1 传统项目 和 互联网项目1.1.2 互联网项目特点1.1.3 大型互联网项目架构…

【2022.12.17】备战春招Day12——每日一题 + 76. 最小覆盖子串 + 24. 两两交换链表中的节点

【每日一题】1764. 通过连接另一个数组的子数组得到一个数组 题目描述 给你一个长度为 n 的二维整数数组 groups &#xff0c;同时给你一个整数数组 nums 。 你是否可以从 nums 中选出 n 个 不相交 的子数组&#xff0c;使得第 i 个子数组与 groups[i] &#xff08;下标从 0…

C++绘制菱形(曼哈顿距离求解:贼快!!!)

思路&#xff1a; 1.双重for循环遍历输出是最基本的 2.了解曼哈顿距离 &#xff08;1&#xff09;菱形的输入一定是奇数 &#xff08;2&#xff09;我们是思想是填充 * 怎么填呢&#xff1f;我们来画图看看 以 3*3 的矩阵为例子&#xff01; 我们来看看 距离是怎么定义的…

LInux进程优先级和nice值

文章目录一 定义描述二 查看nice值2.1 使用top命令交互式查看nice值2.2 使用ps命令查看nice值2.3 查看程序调度策略三 修改nice值3.1 启动特定nice值的进程3.2 更改现有进程的nice级别一 定义描述 大部分情况下&#xff0c;计算机需要运行的进程数超过了计算机拥有CPU的核心数…

[附源码]Python计算机毕业设计后疫情时期社区居民管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

uni-app卖座电影多端开发纪实(二):搭建导航

@创建4个Tab页 创建一个测试页hello 在pages目录上右键,新建页面执行如图操作,即可全自动生成页面组件+页面注册(pages.json)pages.json 中会生成这么一个玩意,就是页面声明了 pages:[{"path" : "pages/hello/hello","style" :

#ubuntu# 自动挂载硬盘 文件无权限Permission denied

操作前&#xff0c;切记做好备份工作。。。。 有时候我们服务器或电脑使用自动挂载硬盘后&#xff0c;发现没有权限。即使使用chmod 666 xxx也无济于事。无法让非root用户对该硬盘具有完全权限。 可以看到挂载的硬盘容量为1T 如果没有数据的话&#xff0c;可以尝试重新格式化硬…