回溯大总结

news2024/12/23 6:13:24

目录

  • 0、基础
    • 什么是回溯?
    • 回溯法解决的问题
    • 回溯模板
  • 1、组合问题
    • 77. 组合
    • 216.组合总和III
    • 17. 电话号码的字母组合
    • 39. 组合总和:
    • 40.组合总和II

0、基础

什么是回溯?

回溯是一种穷举的搜索算法,并不是一个高效的算法,当一些问题暴力搜素也无法穷举的时候就要使用回溯。

回溯法解决的问题都可以抽象为树形结构

回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度就构成了树的深度

本质上是for循环+递归

回溯法解决的问题

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

%;" />

回溯模板

void backtracking(参数) {
    if (终止条件) { // 搜索到了叶子结点
        存放结果; // 子集、某种排列方式、某种切割方式
        return;
    }

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

1、组合问题

77. 组合

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
在这里插入图片描述

from  typing import  List
class Solution:
    def __init__(self):
        self.path = [] # 存放路径
        self.result = [] # 存放符合条件(k个数)的结果的二维数组
    def backtracking(self, n, k, startindex): # 参数n:树的宽度、参数k:遍历的深度、startindex:记录本层递归中集合从哪里开始遍历
        # 终止条件:到达递归深度:叶子节点:path已经收集到了k个元素
        if len(self.path) == k:
            self.result.append(self.path[:])
            return
        # 单层逻辑:for从starindex开始遍历,将结果加入path中,然后递归下一层,一直到找到叶子节点,然后返回答案,并撤销处理过程
        for i in range(startindex, n+1):
            self.path.append(i)
            self.backtracking(n, k, i+1)
            self.path.pop()
        # 正常情况每一层[i,n]
        # 但是要考虑到剩余元素不满足k的情况,n = 4 ,k = 4 ,i = 2, 剩余元素个数为n-i+1 = 3,现在记录的个数为len(path)
        # n - i +1 + len(path)>= k  -> i <= n - k + 1 +len(path)
        # i \in [startindex,n - k + 1 +len(path)]
        for i in range(startindex, n - (k - len(self.path)) + 2):
            self.path.append(i) 
            self.backtracking(n,k,i+1)
            self.path.pop()        

    def combine(self, n: int, k: int) -> List[List[int]]:
        self.backtracking(n, k, 1)
        return self.result

🌟在for i in range(startindex, n+1):这里面的startindex保证的是树层去重(防止出现[1,2]和[2,1]的情况,组合问题特性
🌟在 self.backtracking(n, k, i+1):这里面的i+1保证的是树枝去重(防止出现[1,1]的情况,也就是同一个元素取了两次,这是一个元素只能出现一次的特性

😙剪枝优化:当目前剩余可以选择的元素不够k的时候就没必要继续了,n - i +1 + len(path)>= k -> i <= n - k + 1 +len(path)
在这里插入图片描述
❤️为什么是path[:]不是path?

  • 浅拷贝vs引用
  • path[:] 是浅拷贝,之后对 self.path 的修改不会影响已经存储在 self.res 中的结果
  • path是引用,如果 self.path 在之后的递归中被修改,那么 self.res 中的结果也会被修改,因为它们指向的是同一个列表对象。

216.组合总和III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。
    在这里插入图片描述
class Solution:
    def __init__(self):
        self.res = []
        self.path = []
    def backtracking(self, n , k, sum, startindex):
        # 剪枝:当前sum已经超过n的节点就不需要继续遍历了
        if sum > n:
            return
        # 终止条件:path的长度为k
        if len(self.path) == k:
            if sum == n: # 如果此时计算的path路径上的值的和为n,就是一种结果组合
                self.res.append(self.path[:])
                return
        # for循环横向遍历,不能有重复的数
        for i in range(startindex, 10):
            sum += i
            self.path.append(i)
            self.backtracking(n, k, sum, i+1)
            sum -= i
            self.path.pop()

    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        self.backtracking(n,k,0,1)
        return self.res

🌟组合问题:for i in range(startindex,n)
🌟无重复元素:backtracking(n,k,i+1)
😙剪枝优化:
1️⃣ 当前总和已经超过n:if sum > n:
2️⃣ 当前剩余元素的数量加上已经记录的元素数量要保证超过k个:9-i+1 + len(path) >=k ➡️ i<=9 - (k - path.size()) + 1

17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
输入:digits = “23”
输出:[“ad”,“ae”,“af”,“bd”,“be”,“bf”,“cd”,“ce”,“cf”]

class Solution:
    def __init__(self):
        self.s = ""
        self.result = []
        self.lettermap = [
            "",     # 0
            "",     # 1
            "abc",  # 2
            "def",  # 3
            "ghi",  # 4
            "jkl",  # 5
            "mno",  # 6
            "pqrs", # 7
            "tuv",  # 8
            "wxyz"  # 9
        ]
    def backtracking(self, digits, index):
        if index == len(digits):
            self.result.append(self.s)
            return
        # 把当前处理的字符“2”变成数字2
        digit = int(digits[index])
        stringletter = self.lettermap[digit]
        for letter in stringletter:
            self.s += letter
            self.backtracking(digits, index + 1)
            self.s = self.s[:-1]

    def letterCombinations(self, digits: str) -> List[str]:
        if not digits:
            return []
        self.backtracking(digits, 0)
        return self.result

39. 组合总和:

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。

class Solution:
    def __init__(self):
        self.path = []
        self.res = []
        self.sum = 0
    def backtracking(self,candidates,target,startindex):
        if self.sum > target:
            return
        if self.sum == target:
            self.res.append(self.path[:])
            return 
        for i in range(startindex,len(candidates)):
            self.sum += candidates[i]
            self.path.append(candidates[i])
            self.backtracking(candidates,target,i)
            self.sum -= candidates[i]
            self.path.pop()

    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        self.backtracking(candidates,target,0)
        return self.res
    

🌟组合问题:for i in range(startindex,n)
🌟可以重复元素:backtracking(n,k,i)

40.组合总和II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明: 所有数字(包括目标数)都是正整数。解集不能包含重复的组合。

🔴数组中含有重复元素
🔴每个元素只能用一次➡️组合中同一元素不能重复
🔴组合问题

class Solution:
    def backtracking(self, candidates, target, total, startIndex, used, path, result):
        if total == target:
            result.append(path[:])
            return

        for i in range(startIndex, len(candidates)):
            # 对于相同的数字,只选择第一个未被使用的数字,跳过其他相同数字
            if i > startIndex and candidates[i] == candidates[i - 1] and not used[i - 1]:
                continue

            if total + candidates[i] > target:
                break
            total += candidates[i]
            path.append(candidates[i])
            used[i] = True
            self.backtracking(candidates, target, total, i + 1, used, path, result)
            used[i] = False
            total -= candidates[i]
            path.pop()

    def combinationSum2(self, candidates, target):
        used = [False] * len(candidates)
        result = []
        candidates.sort()
        self.backtracking(candidates, target, 0, 0, used, [], result)
        return result

🌟组合问题:for i in range(startindex,n)
🌟无重复元素:backtracking(n,k,i+1)
☀️因为初始数组[1,1,2,3]存在重复元素,有可能出现这样的情况,[1(0),2,3][1(1),2,3]、虽然满足无重复元素(原数组的不同元素),但是生成的新组合还是重复了,so,需要删除这种重复
if i > 0 and candidates[i] == candidates[i - 1] and used[i - 1] == false:

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

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

相关文章

高并发内存池(五):ThreadCache、CentralCache和PageCache的内存回收机制、阶段性代码展示和释放内存过程的调试

目录 ThreadCache的内存回收机制 补充内容1 补充内容2 补充内容3 补充内容4 ListTooLong函数的实现 CentralCache的内存回收机制 MapObjectToSpan函数的实现 ReleaseListToSpans函数的实现 PageCache的内存回收机制 补充内容1 补充内容2 ReleaseSpanToPageCache函…

【Spine】引入PhotoshopToSpine脚本

引入 右键Photoshop图标&#xff0c;选择属性 打开文件所在位置 找到目录下的\Presets\Scripts文件夹。 找到Spine目录下的\scripts\photoshop文件夹下的PhotoshopToSpine.jsx 复制它&#xff0c;丢到Photoshop刚才找的那个目录下。 使用 打开.psd文件&#xff0c;检查不要…

二叉树:总结篇!【需要掌握的二叉树技能都在这里啦】

文章目录 前言二叉树理论基础二叉树理论基础二叉树的遍历方式深度优先遍历广度优先遍历 N叉树的遍历方式求二叉树的属性二叉树&#xff1a;是否对称二叉树&#xff1a;求最大深度二叉树&#xff1a;求最小深度二叉树&#xff1a;求有多少个节点二叉树&#xff1a;是否平衡二叉树…

外贸财务软件精选,提升管理效率与精准度

ZohoBooks、QuickBooks等六款会计软件各具特色&#xff0c;支持多币种、国际化等功能&#xff0c;适合不同规模外贸企业。其中&#xff0c;ZohoBooks功能全面&#xff0c;QuickBooks操作简便&#xff0c;SageIntacct适合复杂业务&#xff0c;用友U8和金蝶K/3面向中大型企业&…

CommandLineRunner 和 ApplicationRunner

CommandLineRunner 和 ApplicationRunner 背景&#xff1a; 项目启动之前&#xff0c;预先加载数据。比如&#xff0c;权限容器、特殊用户数据等。通常我们可以使用监听器、事件来操作。但是&#xff0c;springboot提供了一个简单的方式来实现此类需求&#xff0c;即&#xf…

《Linux从小白到高手》理论篇(九):Linux的资源监控管理

本篇介绍Linux的资源监控管理。 1、CPU 资源管理 进程调度&#xff1a; Linux 采用公平的进程调度算法&#xff0c;确保每个进程都能获得合理的 CPU 时间。调度算法会根据进程的优先级、等待时间等因素来决定哪个进程获得 CPU 使用权。 可以通过调整进程的优先级来影响其获得…

C++继承实例讲解

C类继承的基本概念 base class&#xff0c;基类、父类 derived class&#xff0c;派生类、子类 C中的类可以扩展&#xff0c;创建保留基类特征的新类&#xff0c;这个过程称之为继承。类继承也可以描述为&#xff1a;派生类继承基类的成员&#xff0c;并在其上添加自己的成员…

【hot100-java】【单词搜索】

回溯 回溯可以使用DFS剪枝解决 class Solution {public boolean exist(char[][] board, String word) {char[] wordsword.toCharArray();for(int i0;i<board.length;i){for(int j0;j<board[0].length;j){if(dfs(board,words,i,j,0)) return true;}}return false;}boolean…

关于Elastic Search与MySQL之间的数据同步

目录 前言 思路分析 同步调用 异步通知 监听binlog 选择 实现数据同步 思路 运行项目 声明交换机、队列 1&#xff09;引入依赖 2&#xff09;声明队列交换机名称 3&#xff09;声明队列交换机 发送MQ消息 接收MQ消息 前言 Elastic Search中的酒店数据来自于MyS…

TypeScript 算法手册【插入排序】

文章目录 TypeScript 算法手册 - 插入排序1. 插入排序简介1.1 插入排序定义1.2 插入排序特点 2. 插入排序步骤过程拆解2.1 选择当前元素2.2 寻找插入位置2.3 插入元素 3. 插入排序的优化3.1 二分查找插入排序案例代码和动态图 4. 插入排序的优点5. 插入排序的缺点总结 【 已更新…

48.哀家要长脑子了!

1.376. 摆动序列 - 力扣&#xff08;LeetCode&#xff09; 看问题抓本质 本质&#xff01;&#xff01;识别和追踪数组中元素值的变化趋势。摆动序列是什么&#xff0c;什么是摆动序列&#xff0c;就是差值正负正负的来&#xff0c;最后要求摆动序列的子序列的长度的话&#x…

如何在KEIL的Debug模式下导出数据

我们知道&#xff0c;利用Keil编写程序时&#xff0c;可以实时显示数据的值&#xff0c;如上图所示&#xff0c;实时显示Voltage和fre的值&#xff0c;那如何导出该数据呢&#xff0c;下边进行详细说明。 首先&#xff0c;进入Debug模式&#xff0c;点击调试里边的函数编辑器。…

计算机毕业设计 基于Python的摄影平台交流系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

GPU、AI、CUDA

文章目录 1.千层面层多层 2. CPU与GPU架构差异3.大规模矩阵操作4.专为并行计算设计的库 1.千层面 神经网络的本质是千层面&#xff0c;由一层一层的线性代数方程组成&#xff0c;每个方程都表示一段数据与另一段数据相关的可能性 层 神经网络的每一次层可以看作是一次线性代…

泰勒图 ——基于相关性与标准差的多模型评价指标可视化比较-XGBoost、sklearn

1、基于相关性与标准差的多模型评价指标可视化比较 # 数据读取并分割 import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split plt.rcParams[font.family] = Times New Roman plt.rcParams[axes.unic…

工单管理系统功能解析,企业运营效率提升利器

工单管理系统如ZohoDesk提供工单生成分配、跟踪、数据分析、客户服务管理及移动兼容等功能&#xff0c;提升效率、增强服务、便于监管和降低成本&#xff0c;是现代企业信息化建设的重要部分。 一. 工单管理系统一般有哪些功能 1. 工单生成与分配 工单管理系统的基础功能是创…

Webstorm 中对 Node.js 后端项目进行断点调试

首先&#xff0c;肯定需要有一个启动服务器的命令脚本。 然后&#xff0c;写一个 debug 的配置&#xff1a; 然后&#xff0c;debug 模式 启动项目和 启动调试服务&#xff1a; 最后&#xff0c;发送请求&#xff0c;即可调试&#xff1a; 这几个关键按钮含义&#xff1a; 重启…

8.数据结构与算法-双向链表

双向链表的结构定义 从第二个指针找到下一个元素 从第一个指针找到上一个元素 双向循环列表 从第二个指针找到下一个元素&#xff0c;第二个指针可以往前循环找到链表开头 从第一个指针找到上一个元素&#xff0c;第一个指针可以往前循环昭侯链表结尾 双向链表的插入 双向链…

NLP:BERT的介绍并使用该模型计算文本相似度

1. BERT 1.1 Transformer Transformer架构是一种基于自注意力机制(self-attention)的神经网络架构&#xff0c;它代替了以前流行的循环神经网络和长短期记忆网络&#xff0c;已经应用到多个自然语言处理方向。   Transformer架构由两个主要部分组成&#xff1a;编码器(Encod…

uniapp中实现评分组件,多用于购买商品后,对商品进行评价等场景

前言 uni-rate是uniapp框架中提供的一个评分组件。它可以用于用户评价、打分等场景。uni-rate组件可以根据设定的星星总数&#xff0c;展示用户评分的效果&#xff0c;用户可以通过点击星星或滑动星星的方式进行评分。同时&#xff0c;uni-rate组件也支持自定义星星图标、星星…