穷举深搜暴搜回溯剪枝(2)

一)电话号码的字母组合

17. 电话号码的字母组合 - 力扣(LeetCode)

1)画出决策树:只是需要对最终的决策树做一个深度优先遍历

把图画出来,知道每一层在干什么,代码就能写出来了

 2)定义全局变量:

1)定义一个哈希表来处理数字和字符串的映射关系

2)使用ret来保存最终结果

class Solution {
    HashMap<Character,String> result=new HashMap<>();
    List<String> ret;
    StringBuilder path;
    public List<String> letterCombinations(String digits) {
        this.path=new StringBuilder();
        result.put('2',"abc");
        result.put('3', "def");
        result.put('4', "ghi");
        result.put('5', "jkl");
        result.put('6', "mno");
        result.put('7', "pqrs");
        result.put('8', "tuv");
        result.put('9', "wxyz");
        this.ret=new ArrayList<>();
        if(digits.length()==0){
            return ret;
        }
    dfs(result,digits,0);
    return ret;
    }
public void dfs(HashMap<Character,String> result,String digits,int index){
        if(index==digits.length()){
//这个最终条件,第一次就写错了,index遍历到数组的最后一个位置
            ret.add(path.toString());
            return;
        }
       Character number=digits.charAt(index);
       String string=result.get(number);
       char[] chars=string.toCharArray();
       for(int i=0;i<chars.length;i++){
           path.append(chars[i]);
//继续遍历到下一层
           dfs(result,digits,index+1);
//恢复现场,回退到上一层
           path.deleteCharAt(path.length()-1);
       }
    }
}

二)括号生成:

22. 括号生成 - 力扣(LeetCode)

先进行想一下,什么是有效的括号组合?

1)左括号的数量==有括号的数量

2)从头开始的任意一个子串中,左括号的数量都是大于等于右括号的数量,如果发现右括号的数量是大于左括号的,那么必然会出现一个右括号没有做括号匹配,所以就不是一个有效的括号组合

假设如果n==3,那么我们只是需要枚举6个位置即可,因为n==3表示的是三对括号,一共有6个位置,那么我们只是需要进行暴力枚举6个位置可能出现的情况就可以了

一)画出决策树:

 二)定义一个全局变量: 

1)left表示左括号的数量,right表示右括号的数量,N表示一共有多少对括号

2)当进行深度优先遍历的时候,使用这个path变量来进行记录这个路径中曾经出现过的括号

三)dfs所干的事:

当左括号合法的时候,把左括号添加到path中当右括号合法的时候,把右括号添加到path中

四)递归:遇到叶子节点的时候,将这个path添加到ret中,遇到叶子节点的情况就是当right==N的时候,说明此时右括号已经满了

class Solution {
    int N;
    StringBuilder path;
    List<String> ret;
    int right=0;
    int left=0;
    public List<String> generateParenthesis(int n) {
        N=n;
        path=new StringBuilder();
        ret=new ArrayList<>();
        dfs();
        return ret;
    }
    public void dfs(){
         if(right==N){
             ret.add(path.toString());
             return;
         }
    //选择左括号
    if(left<N){
         path.append('(');
         left++;
         dfs();
         //回溯恢复现场
         left--;
         path.deleteCharAt(path.length()-1);
      }
    if(right<left){
        path.append(')');
        right++;
        dfs();
        //回溯恢复现场
        right--;
        path.deleteCharAt(path.length()-1);
    }
    }
}

其实本质上来说把刚才的那些全局变量放到参数里面,只是回溯时候的操作是不一样的

三)组合:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

1)实现剪枝的操作:当我们进行枚举当前数的时候,只需要从当前数的下一个数字开始进行枚举就可以了,只需要控制dfs的参数即可

2)dfs做的事:从pos位置开始进行枚举,将枚举的情况添加到path中即可

3)回溯:当枚举完成当前位置向上归的过程中,先把path中添加的数进行剔除,然后再返回到上一层即可

4)递归出口:k==path.size()

class Solution {
    List<List<Integer>> ret;
    List<Integer> path;
    int N;
    int k;
    public List<List<Integer>> combine(int n, int k) {
        N=n;
        this.k=k;
        path=new ArrayList<>();
        ret=new ArrayList<>();
        dfs(1);
       return ret;
    }
    public void dfs(int index){
         if(path.size()==k) {
            ret.add(new ArrayList<>(path));
            return;
        }
    for(int i=index;i<=N;i++){
        path.add(i);
        dfs(i+1);
        path.remove(path.size()-1);
    }
    }
}

四)目标和

494. 目标和 - 力扣(LeetCode)

中心思想:

1)就是在第一个数前面+1或者是进行-1操作,变量作为全局变量还是函数中的参数来说,作为全局变量是需要考虑一下回溯,作为参数的话是不需要进行考虑回溯操作的,因为代码本身已经帮助我们递归向下进行向上返回的过程中已经帮助我们完成回溯操作了

2)当作为参数的时候,递归函数本身归的过程中已经在帮助我们进行回溯操作了,当int作为参数,数组类型作为全局的时候写法是比较方便的

class Solution {
    int count=0;
    int target=0;
    int path=0;
    public int findTargetSumWays(int[] nums, int target) {
        this.target=target;
        dfs(nums,0);
        return count;
    }
    public void dfs(int[] nums,int pos){
        if(pos==nums.length){
            if(path==target) count++;
            return;
        }
//当前这个数取正数的情况
        path+=nums[pos];//pos的作用是记录数组下标
        dfs(nums,pos+1);
        //回溯
        path-=nums[pos];
//当前这个数取负数的情况
        path-=nums[pos];
        dfs(nums,pos+1);
        //回溯
        path+=nums[pos];
    }
}
class Solution {
    int count=0;
    int target=0;
    public int findTargetSumWays(int[] nums, int target) {
        this.target=target;
        dfs(nums,0,0);
        return count;
    }
   public void dfs(int[] nums,int path,int pos){
         if(pos==nums.length){
             if(path==target){
                 count++;
             }
             return;
         }
         dfs(nums,path+nums[pos],pos+1);//回来的就是原来的现场
         dfs(nums,path-nums[pos],pos+1);
   }
}

五)组合总和(1):

39. 组合总和 - 力扣(LeetCode)

1)没有一成不变的模板,只需要将决策树画出来,将最终的决策树转化成代码即可

2)反正这个题就是你从目标的元素里面找到几个数这几个数进行相加的结果等于target即可

3)所以这个题的中心思想就是一个数一个数,两个数,两个数的进行考虑

4)第一个位置可以存放2,可以存放3,可以存放5

解法1:

class Solution {
    List<List<Integer>> ret;
    int sum=0;
    List<Integer> path;
    int target=0;
    public List<List<Integer>> combinationSum(int[] nums, int target) {
        ret=new ArrayList<>();
        path=new ArrayList<>();
        this.target=target;
        Arrays.sort(nums);
        dfs(nums,0);
        return ret;
    }
    public void dfs(int[] nums,int index){
        if(sum==target){
            ret.add(new ArrayList<>(path));
            return;
        }
//index这个参数表示的是这一层循环从数组中的哪一个位置开始进行枚举
    for(int i=index;i<nums.length;i++){
        path.add(nums[i]);
        sum+=nums[i];
         if(sum>target){
             sum-=nums[i];
             path.remove(path.size()-1);
//如果说当前的数加起来的和已经都大于target了,那么后续都无需进行遍历了,因为此时数组是有序的,直接回退到上一层即可
             return;
         } 
        dfs(nums,i);//下一次遍历的位置还是可以从当前位置开始进行遍历,这里面是可以选择重复元素
        sum-=nums[i];
        path.remove(path.size()-1);
    }
 }
}

class Solution {
    List<List<Integer>> ret;
    int sum=0;
    List<Integer> path;
    int target=0;
    public List<List<Integer>> combinationSum(int[] nums, int target) {
        ret=new ArrayList<>();
        path=new ArrayList<>();
        this.target=target;
        Arrays.sort(nums);
        dfs(nums,0);
        return ret;
    }
    public void dfs(int[] nums,int index){
        if(sum>target) return;
        if(sum==target){
            ret.add(new ArrayList<>(path));
            return;
        }
    for(int i=index;i<nums.length;i++){
        path.add(nums[i]);
        sum+=nums[i];
        dfs(nums,i);
//枚举到下一层的时候回溯到当前结点的时候进行恢复现场的操作
        sum-=nums[i];
        path.remove(path.size()-1);
    }
 }
}

下面是当sum是局部变量的时候,所进行传递的值:

 解法2:根据选取数的个数来画出决策树:

每一个数我可以选择0个,1个,可以选择2个,可以选择3个,可以选择4个

我们告诉dfs一个位置,枚举1个,两个,三个,只要小于8即可,只要枚举的个数*这个数<8即可,添加到path路径中,接下来去下一层开始走

当我们枚举完9的时候才会向上恢复现场的,因为假设如果枚举到3的时候才向上恢复现场,那么6的情况必定会被丢弃掉,因为后面的数是在前面的数的基础上+3的,后面的数要依赖于前面的数,所以当我们将这一层的所有的数处理完成之后才会恢复现场
class Solution {
    List<List<Integer>> ret;
    List<Integer> path;
    int target=0;
    int sum=0;
    public List<List<Integer>> combinationSum(int[] nums, int target) {
        ret=new ArrayList<>();
        path=new ArrayList<>();
        this.target=target;
        dfs(nums,0);
       return ret;
    }
    public void dfs(int[] nums,int index){
         if(sum>target){
            return;
         } 
         if(sum==target){
             ret.add(new ArrayList<>(path));
             return;
         }
         if(index==nums.length) return;
         int pos=0;
    for(int i=0;sum<target;i++){
          if(i!=0){
              path.add(nums[index]);
              sum+=nums[index];
              pos++;
          }
          dfs(nums,index+1);
        }
        while(pos>0){
          sum-=nums[index];
          path.remove(path.size()-1);
          pos--;//记录加了多少次
      }
    }
}

六)组合总和(2) 

39. 组合总和 - 力扣(LeetCode)

class Solution {
    int sum=0;
    int target=0;
    List<List<Integer>> ret;
    List<Integer> path;
    boolean[] used;
    public List<List<Integer>> combinationSum2(int[] nums, int target) {
         this.ret=new ArrayList<>();
         this.path=new ArrayList<>();
         this.target=target;
         this.used=new boolean[nums.length];
         Arrays.sort(nums);
         dfs(nums,0);
         return ret;
    }
    public void dfs(int[] nums,int index){
        if(sum>target) return;
        if(sum==target){
            ret.add(new ArrayList<>(path));
            return;
        }
     for(int i=index;i<nums.length;i++){
         if(i!=index&&used[i-1]!=true&&nums[i]==nums[i-1]){
             continue;
         }
         path.add(nums[i]);
         sum+=nums[i];
         used[i]=true;
         dfs(nums,i+1);
         used[i]=false;
         sum-=nums[i]; 
        path.remove(path.size()-1);
     }
    }
}

七)路经总和

112. 路径总和 - 力扣(LeetCode)

解法1:找到二叉树的所有路径和,把他存放到一个list里面

相同子问题:跟定一个根节点,求以根节点为树的所有路径和

递归出口:当进行遍历到叶子结点的时候

dfs:向下一直找到叶子节点,如果遇到叶子节点,就把sum+root.val存放到最终结果中

class Solution {
    public int sum=0;
    List<Integer> list=new ArrayList<>();
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root==null) return false;
        dfs(root,0);
        return list.contains(targetSum);
    }
    public void dfs(TreeNode root,int sum){
       if(root.left==null&&root.right==null){
//与到叶子节点的时候才会进行计数,找到递归的出口
           list.add(sum+root.val);
           return;
       }
        if(root.left!=null) dfs(root.left,sum+root.val);
        if(root.right!=null) dfs(root.right,sum+root.val);
    }
}

解法2:使用回溯的方式,也是找到重复子问题:

1)观察我们要找的所有函数,我们可以查询出它的功能:询问是否从当前节点root到达叶子节点的路径,找到满足于路径和等于sum,假设从根节点到达当前节点的和是temp,那么我们只是需要找到是否从当前节点到达叶子节点,找到和等于sum-temp的叶子的路径,满足路径之和等于sum-temp

2)递归出口:当遍历到叶子结点的时候,判断sum-temp==true

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root==null) return false;
        if(root.left==null&&root.right==null){
            return root.val==targetSum;
        }
return hasPathSum(root.left,targetSum-root.val)||hasPathSum(root.right,targetSum-root.val);
    }
}

八)路经总和(2)

class Solution {
    List<Integer> path;
    List<List<Integer>> ret;
    int targetSum;
    int sum;
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        this.path=new ArrayList<>();
        this.ret=new ArrayList<>();
        this.targetSum=targetSum;
        dfs(root);
     return ret;
    }
    public void dfs(TreeNode root){
        if(root==null) return;
        if(root.left==null&&root.right==null){
            path.add(root.val);
            sum+=root.val;
            if(sum==targetSum){
               ret.add(new ArrayList<>(path));
            }
            sum-=root.val;
            path.remove(path.size()-1);
//此时遍历到叶子结点的时候还是需要向上进行回溯操作
            return;
        }
    path.add(root.val);
    sum+=root.val;
    dfs(root.left);
//向上回溯,恢复现场
    sum-=root.val;
    path.remove(path.size()-1);
    path.add(root.val);
    sum+=root.val;
    dfs(root.right);
//向上回溯,恢复现场
    sum-=root.val;
    path.remove(path.size()-1);
    }
}

九)组合总和(3)

216. 组合总和 III - 力扣(LeetCode)

class Solution {
    List<Integer> path;
    List<List<Integer>> ret;
    int sum=0;
    int n=0;
    int target;
    public List<List<Integer>> combinationSum3(int n, int target) {
        this.path=new ArrayList<>();
        this.target=target;
        this.n=n;
        this.ret=new ArrayList<>();
        dfs(1);
        return ret;
    }
    public void dfs(int index){
        if(path.size()>n) return;
        if(sum==target&&path.size()==n){
            ret.add(new ArrayList<>(path));
            return;
        }
       for(int i=index;i<=9;i++){
           path.add(i);
           sum+=i;
           dfs(i+1);
           sum-=i;
           path.remove(path.size()-1);
       }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51单片机(普中HC6800-EM3 V3.0)实验例程软件分析 实验一 点亮第一个LED

目录 前言 一、原理图及知识点介绍 1.1、LED原理图 1.2、MCU51原理图 二、代码分析 知识点一&#xff1a;#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器 知识点二&#xff1a;你知道sfr P0 0x80;是怎么来的呢为什么要赋值0x80&#xff…

35.利用fminsearch解 多元变量无约束条件下的函数最小值(matlab程序)

1.简述 1.fminsearch函数基本语法 函数功能&#xff1a;使用无导数法计算无约束多变量函数的最小值 语法 x fminsearch(fun,x0) x fminsearch(fun,x0,options) x fminsearch(problem) [x,fval] fminsearch(___) [x,fval,exitflag] fminsearch(___) [x,fval,exitflag,out…

Java版本spring cloud + spring boot企业电子招投标系统源代码+ 支持二次开+定制化服务

&#xfeff; 电子招标采购软件 解决方案 招标面向的对象为供应商库中所有符合招标要求的供应商&#xff0c;当库中的供应商有一定积累的时候&#xff0c;会节省大量引入新供应商的时间。系统自动从供应商库中筛选符合招标要求的供应商&#xff0c;改变以往邀标的业务模式。招…

Vue3_03_setup函数

1.理解&#xff1a;Vue3.0 中的一个新的配置项&#xff0c;值为一个函数。 2.setup是所有组合式 API 表演的舞台。 3.组件中所用到的&#xff1a;数据、方法等等&#xff0c;均要配置在setup中。 4.setup函数的两种返回值&#xff1a; 若返回一个对象&#xff0c;则对象中的…

两数相加 II

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数字都不会以零开头。 示例1&#xff1a; 输入&#xff1a;l1 [7,2,4,3], l2 [5,6,4] 输…

需要仔细了解公文类型和目的,以便选择合适的写作风格

撰写公文前需要仔细了解公文类型和目的&#xff0c;以便选择合适的写作风格。 不同类型的公文有不同的结构、内容和表达方式&#xff0c;需要根据具体类型和目的来选择合适的写作风格和表达方式。例如&#xff0c;通知、公告等公文需要采用简洁明了、规范严谨的表达方式&#x…

Mr. Cappuccino的第58杯咖啡——MacOS配置Maven和Java环境

MacOS配置Maven和Java环境 查看Mac使用的是哪个shell下载并准备Maven下载Maven配置前准备 下载并安装JDK下载JDK安装JDK 配置Maven和Java环境添加配置加载配置 验证环境 查看Mac使用的是哪个shell echo $SHELL如果使用的是bash&#xff0c;则使用以下命令 open ~/.bash_profi…

SpringBoot+logback默认日志的配置和使用

记录一下SpringBoot2.0.x使用默认logback日志的配置和常见使用 SpringBoot的默认日志是logback&#xff0c;在SpringBoot2.0.x版本中使用logback很方便而且内存开销小&#xff0c;速度快&#xff0c;还不需要去单独的配置maven的jar包&#xff0c;因为已经集成整合了的。作为专…

《吐血整理》高级系列教程-吃透Fiddler抓包教程(21)-如何使用Fiddler生成Jmeter脚本-上篇

1.简介 我们知道Jmeter本身可以录制脚本&#xff0c;也可以通过BadBoy&#xff0c;BlazeMeter等工具进行录制&#xff0c;其实Fiddler也可以录制Jmter脚本&#xff08;而且有些页面&#xff0c;由于安全设置等原因&#xff0c;使用Jmeter直接无法打开录制时&#xff0c;这时就…

数据结构 10-排序4 统计工龄 桶排序/计数排序(C语言)

给定公司名员工的工龄&#xff0c;要求按工龄增序输出每个工龄段有多少员工。 输入格式: 输入首先给出正整数&#xff08;≤&#xff09;&#xff0c;即员工总人数&#xff1b;随后给出个整数&#xff0c;即每个员工的工龄&#xff0c;范围在[0, 50]。 输出格式: 按工龄的递…

第126天:内网安全-隧道技术SSHDNSICMPSMB上线通讯LinuxMac

知识点 #知识点&#xff1a; 1、入站规则不出网上线方案 2、出站规则不出网上线方案 3、隧道技术-SMB&ICMP&DNS&SSH 4、控制上线-Linux&Mac&IOS&Android-连接方向&#xff1a;正向&反向&#xff08;基础课程有讲过&#xff09; -内网穿透&#xf…

JMeter 4.x 简单使用

文章目录 前言JMeter 4.x 简单使用1. 启动2. 设置成中文3. 接口测试3.1. 设置线程组3.2. HTTP信息请求头管理器3.3. 添加HTTP请求默认值3.4. 添加HTTP cookie 管理3.5. 添加http请求3.5.1. 添加断言 3.6. 添加监听器-查看结果树3.7. 添加监听器-聚合报告 4. 测试 前言 如果您觉…

eeglab(自用)

目录 1.加载、显示数据 2.绘制脑电头皮图 3.绘制通道光谱图 4.预处理工具 5.ICA去除伪迹 5. 提取数据epoch 1.加载、显示数据 观察事件值(Event values)&#xff1a;该数据集中包含2400个事件&#xff0c;每个事件指定了EEG.event结构的字段Type(类型)、position(位置)和…

macOS 环境变量加载探究

使用 macOS 安装环境&#xff0c;见到过很数种环境变量配置方法&#xff0c;每次也都是按照别人的代码&#xff0c;人家配置在哪 我就配置在哪&#xff0c;其实不太清楚有什么区别&#xff0c;决定记录下。 本机 macOS 13.3&#xff0c;从 macOS Catalina(10.15) 开始&#xf…

Opencv-C++笔记 (16) : 几何变换 (图像的翻转(镜像),平移,旋转,仿射,透视变换)

文章目录 一、图像平移二、图像旋转2.1 求旋转矩阵2.2 求旋转后图像的尺寸2.3手工实现图像旋转2.4 opencv函数实现图像旋转 三、图像翻转3.1左右翻转3.2、上下翻转3.3 上下颠倒&#xff0c;左右相反 4、错切变换4.1 实现错切变换 5、仿射变换5.1 求解仿射变换5.2 OpenCV实现仿射…

【IDEA+Spark Streaming 3.4.1+Dstream监控套接字流统计WordCount保存至MySQL8】

【IDEASpark Streaming 3.4.1Dstream监控套接字流统计WordCount保存至MySQL8】 把DStream写入到MySQL数据库中 Spark 3.4.1MySQL 8.0.30sbt 1.9.2 文章目录 【IDEASpark Streaming 3.4.1Dstream监控套接字流统计WordCount保存至MySQL8】前言一、背景说明二、使用步骤1.引入库2…

某东详情页h5st 算法分析

文章目录 声明目标地址h5st 算法四大入参分析1. z值生成2. v值生成3. b值生成4. r值生成风控浅谈往期逆向文章推荐声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请私信我立即删除! 目标地址 aHR0cHM6Ly…

IO进程线程day7(2023.8.4)

一、Xmind整理&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;创建两个线程&#xff1a;其中一个线程拷贝前半部分&#xff0c;另一个线程拷贝后半部分。 只允许开一份资源&#xff0c;且用互斥锁方式实现。 提示&#xff1a;找临界区--->找临界资源。 #includ…

单例模式和工厂模式

目录 今日良言&#xff1a;关关难过关关过&#xff0c;步步难行步步行 一、单例模式 1.饿汉模式 2.懒汉模式 二、工厂模式 今日良言&#xff1a;关关难过关关过&#xff0c;步步难行步步行 一、单例模式 首先来解释一下&#xff0c;什么是单例模式。 单例模式也就是单个…

linux 文件的权限

修改文件的权限 我这里有一个test.txt 文件&#xff0c;我们ll 查看一下该文件相应的属性信息 其中&#xff0c;权限的位置是相对固定的即&#xff1a; 第一个位置是r 权限&#xff0c;代表可读权限。 第二个位置是w权限&#xff0c;代表可修改权限。 第三个位置是x权限&…
最新文章