算法笔记(java)——回溯篇

news2024/11/25 23:12:12

回溯算法解决问题最有规律性,借用一下卡哥的图:
在这里插入图片描述

只要遇到上述问题就可以考虑使用回溯,回溯法的效率并不高,是一种暴力解法,其代码是嵌套在for循环中的递归,用来解决暴力算法解决不了的问题,即可以通过回溯控制递归的层数,递归后可以进行回溯操作,这样下一次循环就不会收到上一次的影响。解决回溯问题的关键就在于清楚整个回溯递归过程,毕竟是嵌套在for循环中的递归,最好的办法就是把所有情况画成一个树,这样就比较清晰了。

递归通用模版和常用API

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

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

常用API

  • temp常用LinkedList,因为可以在回溯的时候快速删除最后一个元素,添加使用add,删除使用removeLast
  • 将temp添加进result时,要new一个新的arrayList将temp传入构造器,因为temp是全局共享的,如果不new那么result里没有值
  • 如果需要下一层递归指向下一个数,那么要加参数startIndex,如果是排列问题等,每次从头开始,就不需要了

回溯简单示例

力扣题目:

第77题. 组合

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。

示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]

暴力for循环是没法解决本题的,因为不知道要写多少层,所以使用回溯法。
本题图解:
在这里插入图片描述
本题代码:

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> temp = 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(temp.size() == k){
            result.add(new ArrayList<Integer>(temp));
            return;
        }
        for(int i=startIndex;i<=n-(k-temp.size())+1;i++){
            temp.add(i);
            backTracking(n,k,i+1);
            temp.removeLast(); //  回溯
        }
    }
}

去重问题

去重是使用回溯算法时遇到的最重要的问题,卡哥将去重分为了树枝去重以及数层去重,就是for循环的去重和各层递归的去重,一个横向一个纵向。

最常用的去重思路:
先对原数组排序,使用used数组,对横向for循环去重,对纵向递归不去重。

力扣题目:

40. 组合总和 II

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

本题图示:
在这里插入图片描述
可以看到只要排序好,进行横向去重就行了。used数组的作用就是去重的时候不要误删纵向情况,因为used数组有回溯操作,下一次for循环的used[i-1]是false了已经,而下一层递归是true,以此为条件进行判断。

class Solution {
     // 去重,去重横向的,纵向递归的不去重,要用used[i-1]防止纵向的被去重的时候误删
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> temp = new LinkedList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        if(candidates == null && candidates.length == 0){
            return null;
        }
        Arrays.sort(candidates);
        boolean[] used = new boolean[candidates.length];
        backtracking(candidates,target,0,0,used);
        return result;
    }

    public void backtracking(int[] candidates,int target,int sum,int startIndex,boolean[] used){
        if(sum == target){
            result.add(new ArrayList<>(temp));
            return;
        }
        for(int i=startIndex;i<candidates.length && sum+candidates[i]<=target;i++){
            if(i>0 && candidates[i] == candidates[i-1] && used[i-1] == false){
                continue;
            }
            sum += candidates[i];
            temp.add(candidates[i]);
            used[i] = true;
            backtracking(candidates,target,sum,i+1,used);
            used[i] = false;
            temp.removeLast();
            sum -= candidates[i];

        }
    }
}

还有用used数组去重不了的情况
Set去重
力扣题目:

491. 递增子序列

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

示例 1:

输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

这道题显然是不能用used数组的,因为没法排序,排序后就判断不了递增子序列了。使用hashset或者数组进行去重

class Solution {
    // 不能排序的去重,使用hashset
     List<List<Integer>> result = new ArrayList<>();
     LinkedList<Integer> temp = new LinkedList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        backTracking(nums,0);
        return result;
    }

    public void backTracking(int[] nums,int startIndex){
        if(judge(temp)){
            result.add(new ArrayList<>(temp));
        }
        HashSet<Integer> set = new HashSet<>();
        for(int i= startIndex;i<nums.length;i++){
            if(set.contains(nums[i])){
                continue;
            }
            set.add(nums[i]);
            temp.add(nums[i]); 
            backTracking(nums,i+1);
            temp.removeLast();
        }
    }

    public boolean judge(List<Integer> list){
        if(list.size()<2){
            return false;
        }
        for(int i=0,j=0;j<list.size()-1;i++){
            j=i+1;
            if(list.get(i)>list.get(j)){
                return false;
            }
        }
        return true;
    }
}

每层递归都有自己的hastSet,然后可以对横向for循环去重,因为每一层递归set都不一样,所以不会影响到纵向递归。使用数组同理,和set差不多。

其他问题

排列和组合的区别:

排列是每次递归都是从头开始的:
力扣题目:

46. 全排列

在这里插入图片描述

使用used数组是来判断这个元素有没有在上层递归使用过,毕竟每次都是从0开始

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> temp = new LinkedList<>();
    public List<List<Integer>> permute(int[] nums) {
        boolean[] used =new boolean[nums.length];
        backTracking(nums,used);
        return result;
    }

    public void backTracking(int[] nums,boolean[] used){
        
        if(temp.size() == nums.length){
            result.add(new ArrayList<>(temp));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(used[i] == true){
                continue;
            }
            temp.add(nums[i]);
            used[i] = true;
            backTracking(nums,used);
            used[i] = false;
            temp.removeLast();
        }
    }
}

78.子集和90.子集II属于子集问题,这类问题的特点就是要采集树上每一个出现过的元素,不难,去重逻辑也是上面那样。

还有一些拼接字符串的,需要用StringBuilder和一些stringApi的题目:
131.分割回文串和93.复原IP地址 不算难但是比较恶心。

比较难的就是:
51. N皇后
需要遍历矩阵,但是只需要一个for循环,判定条件比较多
52. 解数独
需要双层循环,判定条件也比较复杂

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

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

相关文章

【机器学习】模型常用评估指标

L0 范数、L1 范数、L2 范数、余弦距离 向量的范数可以简单形象理解为向量的长度&#xff0c;或者向量到零点的距离&#xff0c;亦或是相应两个点之间的距离。因此范数通常会对应一个距离概念。 L0 范数 L0 范数表示向量中非零元素的个数&#xff1a; ∣ ∣ x ∣ ∣ 0 # ( i…

elasticsearch IK分词器

说明&#xff1a;es默认的分词器对中文的识别不是特别好&#xff0c;一段话的分词是一个一个汉字&#xff0c;这显然没有达到想要的结果。 可以安装IK分词器&#xff0c;兼容中文的分词&#xff1b; IK分词器 安装 安装IK分词器&#xff0c;例如elasticsearch的容器名为es&a…

探索Java并发编程利器:LockSupport,一种高效的线程阻塞与唤醒机制

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、用法四、原理五、线程等待和唤醒的方法5.1 Lo…

Microsoft Edge 浏览器的Bing Chat

微软公司持续发力&#xff0c;推出的产品 Bing Chat 与 ChatGPT 之间的竞争愈发激烈。如今&#xff0c;微软不仅不断更新 Edge 浏览器&#xff0c;还将 Bing Chat 内置在边栏中&#xff0c;方便用户快速访问。这一举措不禁让人想起&#xff0c;Edge 浏览器如今已经是一款名副其…

Maven项目的两种打包方式-spring-boot-mavne-plugin/maven-jar-plugin

Maven项目的两种打包方式-spring-boot-mavne-plugin/maven-jar-plugin 1. 前言Maven的两种打包方式 2. 流程图3. spring-boor-maven-plugin打包4. maven-jar-plugin/maven-dependency-plugin打包 1. 前言 Maven的两种打包方式 spring-boot-maven-plugin springboot默认打包方…

新星计划联系人列表管理后台(一)-- 搭建项目开发环境

前言&#xff1a;对于前端的内容目前已经发现了很多大佬写的高质量文章&#xff0c;我这里就没必要再重复去造轮子了。接下来我会记录一下我从零到整完成此项目的过程&#xff0c;类似于日记&#xff0c;我会把我开发的步骤、过程中遇到的问题、如何解决此问题的思路、以及get到…

FPGA+EMMC 8通道存储小板

FPGA 采用XILINX公司A7100作为主芯片 AD采用AD7606及一款陀螺仪传感器&#xff0c;可以实时存储到EMMC&#xff0c;系统分为采集模式及回放模式 通过232接口对工作模式进行配置&#xff0c;采样率可以动态配置 回放采用W5100S通过TCP协议进行回放数据

福格行为模型

福格行为模型 福格行为模型也被称为“上瘾模型”&#xff0c;在工作和生活中应用非常的广泛。在工作中&#xff1a;产品设计、用户运营策略、活动设计、广告设计、游戏设计等等&#xff1b;在生活中&#xff1a;学习习惯培养、持续的健身、减肥计划等等。这些事通常大家以为是…

【N32L40X】学习笔记10-外部触发方式计数

定时器采用外部触发方式计数 也就是外部时钟源模式2 此模式由 TIMx_SMCTRL .EXCEN 选择等于 1。计数器可以在外部触发输入 ETR 的每个上升沿或下降沿 计数。 极性选择分频选择过滤选择选择外部时钟ETR模式 bsp_time_counter_ETR.h #ifndef _BSP_TIME_COUNTER_ETR_H_ #defi…

STM32(HAL库)驱动AD8232心率传感器

目录 1、简介 2、CubeMX初始化配置 2.1 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 ADC外设配置 2.3 串口外设配置 2.4 GPIO配置 2.5 项目生成 3、KEIL端程序整合 3.1 串口重映射 3.2 ADC数据采集 3.3 主函数代码整合 4 硬件连接 5 效果展示 1、简介 本文通过STM32…

【车载性能优化】将线程进程运行在期望的CPU核心上

车载Android应用开发中&#xff0c;可能会出现一种奇葩的要求&#xff1a;与用户交互时应用需要全速运行&#xff0c;保证交互的流畅性&#xff0c;但是如果应用进入后台就需要怠速运行&#xff0c;让出更多的资源保证系统或前台应用的流畅度。那么基于这种需求&#xff0c;我们…

深度学习(31)——DeformableDETR(2)

深度学习&#xff08;31&#xff09;——DeformableDETR&#xff08;2&#xff09; 文章目录 深度学习&#xff08;31&#xff09;——DeformableDETR&#xff08;2&#xff09;1. backbone——Resnet502. neck——Channel mapper3. DeformableDETRHead4. DeformableDetrTransf…

Linux:多进程和多线程回环socket服务器和客户端

多进程socket服务器代码&#xff1a; #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #include <ctype.h> #include <sys/wait.h> #i…

JWT 使用

前端访问后台a系统和b系统访问 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准&#xff08;(RFC 7519).该token被设计为紧凑且安全的&#xff0c;特别适用于分布式站点的单点登录&#xff08;SSO&#xff09;场景。 JWT的本质就是一个…

记录安装DragGAN遇到的问题

首先第一个是安装的问题 安装的envirenment.yml有很多包过时了下载不了&#xff0c;可以参考以下文章写一个requirement.txt来下载依赖环境 --extra-index-url https://download.pytorch.org/whl/cu118 numpy1.23.5 click scipy pillow9.5.0 requests tqdm4.65.0 ninja matpl…

哪些语句会被waf屏蔽?

什么是waf&#xff1a;Web应用防火墙&#xff0c;Web Application Firewall的简称。 waf的功能&#xff1a;WAF可以发现和拦截各类Web层面的攻击&#xff0c;记录攻击日志&#xff0c;实时预警提醒&#xff0c;在Web应 用本身存在缺陷的情况下保障其安全。 封IP &#xff1a;…

Qt应用开发——QLineEdit

目录 一、概述 二、属性和方法 三、信号 一、概述 QLineEdit允许用户输入和编辑单行纯文本&#xff0c;并可以使用快捷编辑功能&#xff0c;包括复制、粘贴、剪切和拖放。是项目开发中最常用的输入控件。 默认键绑定描述如下。 Left Arrow //将光标向左移…

Fourier变换极其应用(Brad G. Osgood)——第1章——Fourier级数

目录 第1章 Fourier级数 1.1 选择&#xff1a;“欢迎入局”(Choices: Welcome Aboard) 1.2 周期性现象(Periodic phenomena) 1.2.1 时间和空间(time and space) 1.2.1.1 时间和空间周期性在波动中最自然地结合在一起 1.2.1.2 更多关于空间的周期性例子 1.2.2 定义&…

11 简单的Thymeleaf语法

11.1 spring-boot环境准备 重要依赖&#xff1a; <!--thymeleaf--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> 11.2 转发消息不转义 就是如…

picgo Request failed with status code 404

今天写picgo的时候&#xff0c;出现了一个错误&#xff0c;如何解决&#xff1a; 这里是repo的配置出现了问题&#xff0c;不过我的是因为粗心&#xff0c;把master写成了mater&#xff0c;emmmm 这里的repo要跟仓库的地址相同就是这一块&#xff1a;把这一块填到repo就行 然…