回溯算法问题汇总

news2024/11/15 17:44:26

文章目录

  • 模版
  • 一. 组合问题
    • 77. 组合
    • 216.组合总和III
    • 17.电话号码的字母组合
    • 39. 组合总和
    • 40.组合总和II
    • 131.分割回文串
    • 93.复原IP地址
    • 78.子集
    • 90.子集II
    • 491.递增子序列
    • 46.全排列
    • 47.全排列 II
    • 332.重新安排行程
    • 51. N皇后
    • 37. 解数独

模版

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

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

在这里插入图片描述

一. 组合问题

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

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

组合是不强调元素顺序的,排列是强调元素顺序。

例如:{1, 2} 和 {2, 1} 在组合上,就是一个集合,因为不强调顺序,而要是排列的话,{1, 2} 和 {2, 1} 就是两个集合了。

记住组合无序,排列有序,就可以了

77. 组合

在这里插入图片描述

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        if(n<k){
            return lists;
        }

        find(n,1,k);
        return lists;
    }

    private void find(int n, int begin,int k){
        if(k == 0){
            lists.add(new ArrayList<>(list));
            return;
        }
        if(begin > n){
            return;
        }
        for(int i=begin;i<=n;i++){
            //如果后面的数不够了,就不用考虑了
            if(n-i+1 < k){
                return;
            }

            // 将当前数放入list
            list.add(i);
            k--;
            find(n,i+1,k);
            k++;
            list.remove(list.size()-1);
        }
    }


    private List<List<Integer>> lists = new ArrayList<List<Integer>>();
    private List<Integer> list = new ArrayList<>();
}

做了简单的剪枝,如果剩余的元素加上已选中的元素,不够凑成k个,那就直接返回。

216.组合总和III

在这里插入图片描述

class Solution {
    public List<List<Integer>> combinationSum3(int k, int n) {
        find(1,k,n);
        return list;
    }

    private void find(int begin,int k, int sub){
        //如果减成了负数,直接返回
        if(sub<0){
            return;
        }
        if(sub==0 && path.size() == k){
            list.add(new ArrayList<>(path));
            return;
        }

        for(int i=begin;i<10;i++){
            // 直接减
            path.add(i);
            sub -= i;
            find(i+1,k,sub);
            sub += i;
            path.remove(path.size()-1);
        }
    }

    private List<List<Integer>> list = new ArrayList<List<Integer>>();
    private List<Integer> path = new ArrayList<Integer>();
}

17.电话号码的字母组合

在这里插入图片描述

class Solution {
    public List<String> letterCombinations(String digits) {
        if(digits == "" || digits == null || digits.length() == 0){
            return new ArrayList<String>();
        }
        
        find(0,digits);
        return list;

    }

    private void find(int index,String digits ){
        if(index == digits.length()){
            list.add(path.toString());
            return;
        }

        String str = map[Integer.parseInt(String.valueOf(digits.charAt(index)))];

        for(int i=0;i<str.length();i++){
            path.append(str.charAt(i));
            find(index+1,digits);
            path.deleteCharAt(path.length()-1);
        }
    }

    // 预先放入数组中,然后根据字符串数字-数组序列号取出
    private String[] map =  new String[]{
    "", // 0
    "", // 1
    "abc", // 2
    "def", // 3
    "ghi", // 4
    "jkl", // 5
    "mno", // 6
    "pqrs", // 7
    "tuv", // 8
    "wxyz", // 9
    };

    private List<String> list = new ArrayList<>();
    private StringBuilder path = new StringBuilder();
}

39. 组合总和

在这里插入图片描述

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        find(0,candidates,target);
        return lists;
    }

    private void find(int begin,int[] candidates,int target){
        if(target == 0){
            lists.add(new ArrayList<>(path));
            return; 
        }
        if(target<0 || begin >= candidates.length){
            return;
        }

        for(int i=begin;i<candidates.length;i++){
            // 剪枝,如果我已经比它小了,后面更大的数你就不用考虑了
            if(candidates[i] > target){
                return;
            }
            target -= candidates[i];
            path.add(candidates[i]);
            // 由于可以重复取数,所以没有i+1
            find(i,candidates,target);
            path.remove(path.size()-1);
            target += candidates[i];
        }
    }

    private List<List<Integer>> lists = new ArrayList<List<Integer>>();
    private List<Integer> path = new ArrayList<Integer>();
}

40.组合总和II

在这里插入图片描述

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        used = new int[candidates.length];
        find(0,candidates,target);
        return lists;
    }

    private void find(int begin, int[] candidates,int target){
        // 如果减完了,结束
        if(target == 0){
            lists.add(new ArrayList(path));
            return;
        }

         // 如果搜完了或者已经成了负数,结束
        if(target<0 || begin >= candidates.length){
            return;
        }


        for(int i=begin;i<candidates.length;i++){
            // 如果是重复的数字,同一层的搜过了就别搜我了,谢谢
            // 比如 2 2 ,如果是同一层的搜过了那它就used=0,但如果是递归下来的,就是used=1
            // 注意continue和return 这个for循环相同于横向的一个遍历,如果到这一步横向的后面节点不用了就return,如果只是跳过这个节点继续去执行横向的下个节点,则用continue
            if(i>0 && candidates[i] == candidates[i-1] && used[i-1]==0){
                continue;
            }

            // 如果我已经比target大了,那就别考虑我了,我后面更大的也别考虑(已排序)
            if(candidates[i] > target){
                return;
            }
            // 如果我被用过了,千万别用我
            if(used[i] == 1){
                continue;
            }

            target-=candidates[i];
            used[i]=1;
            path.add(candidates[i]);
            find(i+1,candidates,target);
            used[i]=0;
            target+=candidates[i];
            path.remove(path.size()-1);
        }
    }

    private List<List<Integer>> lists = new ArrayList<List<Integer>>();
    private List<Integer> path = new ArrayList<Integer>();
    private int[] used;
}

在这里插入图片描述

131.分割回文串

在这里插入图片描述

class Solution {
    public List<List<String>> partition(String s) {
        if(s == null || s.length() == 0){
            return lists;
        }
        find(0,s);
        return lists;
    }

    private void find(int begin, String s){

        // 如果切割到底了,且都是回文,则成功
        if(begin == s.length()){
            lists.add(new ArrayList<>(path));
            return;
        }

        for(int i=begin;i<s.length();i++){
            // 切割i下标
            // 如果非回文,直接跳过,去截取后面的吧
            if(!isReverse(s,begin,i)){
                continue;
            }
            path.add(s.substring(begin,i+1));
            find(i+1,s);
            path.remove(path.size()-1);
        }
    }

    // 判断是否是回文
    private boolean isReverse(String s,int begin, int end) {
        for (int i = begin, j = end; i < j; i++, j--) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }


    private List<List<String>> lists = new ArrayList<List<String>>();
    private List<String> path = new ArrayList<String>();
    
}

在这里插入图片描述

93.复原IP地址

在这里插入图片描述

class Solution {
      public List<String> restoreIpAddresses(String s) {
        //如果字符串长度小于4或大于12,则不符合
        if(s == null || s.length() < 4 || s.length() > 12){
            return list;
        }
        find(0,s);
        return list;

    }

    private void find(int begin,String s){
        // 如果剩余的字符串长度不能满足剩下的ip分配,或者太多了,则结束
        if((s.length()-begin) < (4-path.size()) || (s.length()-begin) > (4-path.size()) * 3){
            return;
        }



        // 如果list长度为3,说明是最后一个了,可以加进去了
        if(path.size()==3){
            String str = s.substring(begin);
            if(!isVaild(str)){
                return;
            }
           
            path.add(str);
            list.add(pathToList(path));
            path.remove(path.size()-1);
            return;
        }

        // 循环切割
        for(int i=begin;i<s.length() && i<=begin+2;i++){

            String str = s.substring(begin,i+1);
            // 如果不满足大小,结束
            if(!isVaild(str)){
                continue;
            }

            path.add(str);
            find(i+1,s);
            path.remove(path.size()-1);
        }

    }

    // 判断切割的字符串是否合法
    private boolean isVaild(String str){
        // 不可用是以0开头的多位数
        if(str.charAt(0) == '0' && str.length() > 1){
                return false;
        }
        // 判断该数字是否不大于255
        return Integer.parseInt(str) <= 255;
    }
    

    // 将path转化为地址ip字符串
    private String pathToList(List<String> path){
        StringBuilder str = new StringBuilder();
        path.forEach(p -> {
            str.append(p).append(".");
        });
        str.deleteCharAt(str.length()-1);
        return str.toString();
    }

    private List<String> list = new ArrayList<String>();
    private List<String> path = new ArrayList<String>();
    private StringBuilder str = new StringBuilder();

}

78.子集

在这里插入图片描述

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
       find(0,nums);
       return list;
    }

    public void find(int begin, int[] nums){
    // 一进来就直接加入,这个的区别就是所有的树节点全部算上
        list.add(new ArrayList<>(path));

       for(int i=begin;i<nums.length;i++){
           path.add(nums[i]);
           find(i+1,nums);
           path.remove(path.size()-1);
       }
    }

    private List<List<Integer>> list = new ArrayList<List<Integer>>();
    private List<Integer> path = new ArrayList<Integer>(); 
}

在这里插入图片描述
之前的题目都是最后的叶子结点就是答案,子集问题是把所有的结点全部算上才行。

90.子集II

在这里插入图片描述

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        used = new int[nums.length];
        find(0,nums);
        return list;
    }


    // 递归枚举nums[0]、nums[1]、、、nums[n-1]这n个数选或不选
    public void find(int begin, int[] nums){
        list.add(new ArrayList<>(path));
        
        for(int i=begin;i<nums.length;i++){
            if(i>0&&nums[i]==nums[i-1]&&used[i-1]==0){
                continue;
            }
            path.add(nums[i]);
            used[i]=1;
            find(i+1, nums);
            used[i]=0;
            path.remove(path.size() - 1);
        }
    }
    private List<List<Integer>> list = new ArrayList<List<Integer>>();
    private List<Integer> path = new ArrayList<Integer>(); 
    int[] used;
}

491.递增子序列

在这里插入图片描述

class Solution {
    public List<List<Integer>> findSubsequences(int[] nums) {
        find(0,nums);
        return list;
    }

    private void find(int begin, int[] nums){

        // 如果当前数大于上一个数,则直接加入path,取的树上所有节点
        if(path.size() >= 2){
            list.add(new ArrayList<>(path));
        }
        // 局部变量,只用于该层的遍历
        int[] used = new int[201];
        for(int i=begin;i<nums.length;i++){
            //在该层的遍历中,相同元素不需要重复再执行
            if(i>0 && used[nums[i]+100]==1){
                continue;
            }
            if(path.size()>0 && nums[i] < path.get(path.size()-1)){
                continue;
            }
            used[nums[i]+100]=1;
            path.add(nums[i]);
            find(i+1,nums);
            path.remove(path.size()-1);
        }
    }

    private List<List<Integer>> list = new ArrayList<List<Integer>>();
    private List<Integer> path = new ArrayList<Integer>(); 
    
}

本题的used是局部变量,每层迭代的时候会重新定义,用于每一层的遍历。
同时,答案是收集到所有的树节点。
不理解的画个树图。

46.全排列

在这里插入图片描述

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        used = new int[nums.length];
        find(0,nums);
        return list;
    }

// 每次从没选中的里面选一个,index无限递增,知道list的size为n
    private void find(int index, int[] nums){
        if(index == nums.length){
            list.add(new ArrayList<>(path));
            return;
        }
        for(int i=0;i<nums.length;i++){
            // 如果未选过,则选
            if(used[i] == 1){
                continue;
             }
            path.add(nums[i]);
            used[i] = 1;
            find(index+1, nums);
            path.remove(path.size()-1);
            used[i] = 0;
        } 
    }



    private List<List<Integer>> list = new ArrayList<List<Integer>>();
    private List<Integer> path = new ArrayList<Integer>(); 
    private int[] used;

}

47.全排列 II

在这里插入图片描述

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        used = new int[nums.length];
        Arrays.sort(nums);
        find(0,nums);
        return list;
    }

    private void find(int index, int[] nums){
        if(path.size() == nums.length){
            list.add(new ArrayList<Integer>(path));
            return;
        }

        for(int i=0;i<nums.length;i++){
            // 如果未使用,赶紧用
            if(used[i] == 1){
                continue;
            }
            if(i>0 && nums[i]==nums[i-1]&&used[i-1]==0){
                continue;
            }
            used[i]=1;
            path.add(nums[i]);
            find(index+1,nums);
            path.remove(path.size()-1);
            used[i]=0;
        }
    }
    private List<List<Integer>> list = new ArrayList<List<Integer>>();
    private List<Integer> path = new ArrayList<Integer>(); 
    private int[] used;
}

332.重新安排行程

在这里插入图片描述
缺乏之前知识,后续再做

51. N皇后

在这里插入图片描述

class Solution {
   public List<List<String>> solveNQueens(int n) {
        // 横向的已占的皇后节点
        used = new int[n];
        String[][] nums = new String[n][n];
        init(nums);
        find(nums,0);
        return list;
    }

    private void find(String[][] nums,int index){
        // 迭代结束
        if(index == nums.length){
            list.add(numToList(nums));
            return;
        }

        // 如果这个点不符合皇后范围,则舍弃
        for(int i=0;i<nums.length;i++){
            if(isValid(nums,index,i)){
                used[i]=1;
                nums[index][i] = "Q";
                // 最重要的地方,这里是index+1!!!!
                find(nums,index+1);
                used[i]=0;
                nums[index][i] = ".";
            }

        }


    }
    private boolean isValid(String[][] chessboard, int row, int col){
        // 单独检查列
        if(used[col]==1){
            return false;
        }

        // 检查45度对角线
        for (int i=row-1, j=col-1; i>=0 && j>=0; i--, j--) {
            if (chessboard[i][j] == "Q") {
                return false;
            }
        }

        // 检查135度对角线
        for (int i=row-1, j=col+1; i>=0 && j<=chessboard.length-1; i--, j++) {
            if (chessboard[i][j] == "Q") {
                return false;
            }
        }
        return true;
    }

    // 初始化二维数组为全部"."
    private void init(String[][] str){
        for (int i = 0; i < str.length; i++) {
            for (int j = 0; j < str.length; j++) {
                str[i][j] = ".";
            }
        }
    }
    // 将数组转变成返回值要求的String
    private List<String> numToList(String[][] str){
        List<String> list = new ArrayList<>();
        for (int i = 0; i < str.length; i++) {
            StringBuilder s = new StringBuilder();
            for (int j = 0; j < str.length; j++) {
                s.append(str[i][j]);
            }
            list.add(s.toString());
        }
        return list;
    }
    private List<List<String>> list = new ArrayList<List<String>>();
    int[] used;  
}

不难,但是复杂。
每次迭代是index+1,而不是之前的i+1,切记!

37. 解数独

在这里插入图片描述
缺乏前面知识,后续再做

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

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

相关文章

Webpack(通俗易懂介绍)

主题&#xff1a;为什么需要webpack&#xff0c;用来干什么&#xff1f; 前言 例如&#xff1a;前端不断的技术更新迭代&#xff0c;为了浏览器更好的兼容到以及项目更好的开发&#xff0c;所有才有需要Webpack来打包代码&#xff0c;本文介绍下&#xff0c;Webpack的定义。 …

无FTTR不千兆,华为星光F30让家中不再有“隐秘的角落”

“恒有二者&#xff0c;余畏敬焉。位我上者&#xff0c;灿烂星空&#xff1b;道德律令&#xff0c;在我心中。”康德《实践理性批判》中&#xff0c;将人对外部世界的探索精神&#xff0c;抽象成了对无尽星空的追逐。以前&#xff0c;光为我们照亮现实世界。现在&#xff0c;人…

vue使用axios发送post请求携带json body参数,后端使用@RequestBody进行接收

前言 最近在做自己项目中&#xff0c;做一个非常简单的新增用户场景&#xff0c;但是使用原生axios发送post请求的时候&#xff0c;还是踩了不少坑的。 唉&#xff0c;说多了都是泪&#xff0c;小小一个新增业务&#xff0c;在自己前后端一起开发的时候&#xff0c;硬是搞了好…

使用QQ聊天机器人上传每日健康日报【Nonebot插件教程】

文章目录前言一、需求分析1.功能需求2.技术需求二、流程分析1.分析请求过程2.分析代码编写过程四、代码编写前言 作为2020级入学的大学生&#xff0c;在疫情的笼罩下步入了大学的校门&#xff0c;到校第一件事就是接到了每日进行健康日报身体情况上报的通知&#xff0c;每日醒…

08 OpenCV腐蚀、膨胀与形态学运算

1 腐蚀 腐蚀操作是一种形态学操作&#xff0c;它用于缩小二值图像中的对象&#xff0c;并去除图像中的噪声和细节。其基本原理是将图像中的每个像素与内核进行比较&#xff0c;如果内核覆盖的区域内所有像素值都为非零值&#xff0c;则该像素保持不变&#xff1b;否则&#xf…

django+vue项目搭建,前后端通信打通,

文章目录django 环境搭建1.创建django骨架项目2.创建应用3.试着启动项目&#xff0c;验证环境OK4.基础配置admin.pysettings.py前端项目搭建1.安装vue-cli2.创建前端项目3.创建时候选什么前端项目结构页面上呈现的内容是怎么来的&#xff1f;这里只说明vue部分曲线救国打通vue和…

Linux软件管理YUM

目录 yum配置文件 创建仓库 yum查询功能 yum安装与升级功能 yum删除功能 yum仓库产生的问题和解决之道 yum与dnf 网络源 YUM就是通过分析RPM的标头数据后&#xff0c;根据各软件的相关性制作出属性依赖时的解决方案&#xff0c;然后可以自动处理软件的依赖属性问题&…

1.1 什么是并发

1.1 什么是并发 并发&#xff1a;指两个或更多独立的活动同时发生。并发在生活中随处可见。我们可以一边走路一边说话&#xff0c;也可以两只手同时做不同的动作。 1.1.1 计算机系统中的并发 当我们提到计算机术语的“并发”&#xff0c;指的是在单个系统里同时执行多个独立…

零入门kubernetes网络实战-15->基于golang编程实现给ns网络命名空间添加额外的网卡

《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 本篇文章视频地址(稍后上传) 本篇文章主要是想通过golang编程来实现&#xff0c;为veth pair链接的网络命名空间添加网卡&#xff0c;配置veth pair的IP 即&#xff0c;使用代码创建一…

002 常见量化交易平台使用

常见的量化交易平台&#xff1a;米筐&#xff0c;BigQuant&#xff0c;优矿&#xff0c;聚宽&#xff0c;掘金。 本文简单介绍其中的米筐量化交易平台。米筐支持Python&#xff0c;Java编写交易策略进行回测。 一、平台使用 1. 注册账号 平台网址&#xff1a;米筐量化平台 平…

linux内存申请

一、基本概念 1、页&#xff1a;struct page &#xff0c;如下图所示&#xff0c;x86架构下一般为4K为大小 2、分区&#xff1a;struct zone &#xff0c;如下图所示&#xff0c;x86架构下分为三个区ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM 3、ZONE_DMA&#xff0c;一般由于内存…

代码随想录算法训练营第二天 | 977.有序数组的平方 、209.长度最小的子数组 、59.螺旋矩阵II、总结

打卡第二天&#xff0c;认真做了两道题目&#xff0c;顶不住了好困&#xff0c;明天早上练完车回来再重新看看。 今日任务 第一章数组 977.有序数组的平方209.长度最小的子数组59.螺旋矩阵II 977.有序数组的平方 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每…

知识图谱构建技术综述

摘要 *知识图谱为实现语义化智能搜索以及知识互联打下了基础&#xff0c;。&#xff0c; *随着知识的发展&#xff0c;传统的基于模板和规则构建的知识图谱已经被深度学习所替代。 知识组织得原则中&#xff1a;知识的充分性、有序性和标准化规则。深度学习的效果在很大程度上…

线性神经网络(线性回归)

线性回归 目录线性回归导包生成数据集观察散点图读取数据集初始化模型参数定义模型定义损失函数定义优化算法训练简易实现(pytorch)生成数据集读取数据集定义模型初始化模型参数定义损失函数定义优化算法训练导包 import random import torch from d2l import torch as d2l生成…

微服务相关概念

一、谈谈你对微服务的理解&#xff0c;微服务有哪些优缺点&#xff1f;微服务是由Martin Fowler大师提出的。微服务是一种架构风格&#xff0c;通过将大型的单体应用划分为比较小的服务单元&#xff0c;从而降低整个系统的复杂度。优点&#xff1a;1、服务部署更灵活&#xff1…

Python pickle模块:实现Python对象的持久化存储

Python 中有个序列化过程叫作 pickle&#xff0c;它能够实现任意对象与文本之间的相互转化&#xff0c;也可以实现任意对象与二进制之间的相互转化。也就是说&#xff0c;pickle 可以实现 Python 对象的存储及恢复。值得一提的是&#xff0c;pickle 是 python 语言的一个标准模…

2023美赛A题:受旱灾影响的植物群落

文章目录背景要求服务词汇表背景 不同的植物对压力有不同的反应。例如&#xff0c;草原对干旱非常敏感。干旱以不同的频率和严重程度发生。大量的观察表明&#xff0c;不同物种的数量对植物群落如何在连续多代干旱周期中适应起到了重要作用。在一些仅有一种植物的群落中&#…

Vue基础13之浏览器本地存储、TodoList本地存储、组件自定义事件

Vue基础13浏览器本地存储localStorage 本地存储sessionStorage 会话存储总结TodoList本地存储App.vue组件自定义事件子组件给父组件传值使用props方法App.vueSchool.vue子组件给父组件传值使用组件自定义事件——绑定第一种写法&#xff1a;使用v-on或App.vueStudent.vue第二种…

idea快捷编码:生成for循环、主函数、判空非空、生成单例方法、输出;自定义快捷表达式

前言 idea可根据输入的简单表达式进行识别&#xff0c;快速生成语句 常用的快捷编码&#xff1a;生成for循环、主函数、判空非空、生成单例方法、输出 自定义快捷表达式 博客地址&#xff1a;芒果橙的个人博客 【http://mangocheng.com】 一、idea默认的快捷表达式查看 Editor…

String对象的创建和比较

String类的概述 String类&#xff1a;代表字符串。 Java 程序中的所有字符串字面值&#xff08;如 “abc” &#xff09;都作 为此类的实例实现。 String是JDK中内置的一个类&#xff1a;java.lang.string 。 String表示字符串类型&#xff0c;属于引用数据类型&#xff0c;不…