leetcode刷题记录:暴力搜索算法01 - 回溯

参考:labuladong的算法小抄 https://labuladong.online/algo/essential-technique/backtrack-framework/
这篇太牛了,一个模板把所有的排列组合子集问题全秒了。

1. 简介

暴力搜索算法:回溯、dfs、bfs。这些都可以看做是从二叉树算法衍生出来的。

解决一个回溯问题,实际上是在遍历一颗决策树的过程。树的每个叶子结点上存着一个答案。把整棵树遍历一遍,把叶子结点上的答案都收集起来,就能得到所有的合法答案。
回溯算法的框架

result = []
def backtrack(路径,选择列表):
    if 满足条件:
        result.add(路径)
        return
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

核心是for循环里的递归,在递归前做选择,递归后撤销选择。

2. 全排列问题

leetcode 46
https://leetcode.cn/problems/permutations/
时间复杂度: O(n!)

class Solution(object):
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if not nums:
            return
        used = [False] * len(nums)
        self.res = []
        self.backtrack(nums, [], used)
        return self.res
    def backtrack(self, nums, track, used):
        if len(track) == len(nums):
            self.res.append(track[:])
            return
        for i, n in enumerate(nums):
            if used[i]:
                continue
            track.append(n)
            used[i] = True
            self.backtrack(nums, track, used)
            track.pop()
            used[i] = False

3. N皇后问题

https://leetcode.cn/problems/n-queens/

  • 回溯思想:棋盘的每一行代表决策树的每一层。每个节点可以做出的选择是,在该行的任意一个位置放置一个皇后。
  • isValid只需要检查上方、左上方和右上方的情况。下面的棋盘都是空的
class Solution(object):
    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        if n == 0:
            return 0
        board = [["." for _ in range(n)] for _ in range(n)]
        self.res = []
        self.backtrack(board, 0)
        return self.res
    def backtrack(self, board, row):
        if row == len(board):
            self.res.append([''.join(x[:]) for x in board])
            return
        n = len(board)
        for col in range(n):
            if self.isValid(board, row, col):
                board[row][col] = 'Q'
                self.backtrack(board, row+1)
                board[row][col] = '.'
    
    def isValid(self, board, row, col):
        n = len(board)
        # 检查列
        for i in range(row):
            if board[i][col] == 'Q':
                return False
        # 检查左上方
        r,c = row-1, col-1
        while r >=0 and c >= 0:
            if board[r][c] == 'Q':
                return False
            r -= 1
            c -= 1
        # 检查右上方
        r,c = row-1, col+1
        while r >=0 and c <= n-1:
            if board[r][c] == 'Q':
                return False
            r -= 1
            c += 1
        return True

4. 所有排列、组合、子集问题

4.1 问题抽象

排列、组合、子集问题,都是给定序列nums,从中选出若干个元素的问题。

  • 形式一:nums的元素无重复,不可重复选。
  • 形式二:nums的元素有重复,不可重复选。
  • 形式三:nums的元素无重复,可以重复选。
    作图:
    子集、组合数:
    排列数:

4.2 leetcode 78 子集

https://leetcode.cn/problems/subsets/
nums元素无重复,不可重复选
决策树图:
在这里插入图片描述

  1. 用start + for循环控制子集元素不重复。
  2. 其实这里就相当于整个决策树的遍历。self.res.append(self.track[:])在前序位置将结果插入到res中;for循环就是遍历所有的子树。这里通过start来控制不会收集重复的元素。
class Solution(object):
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        self.res = []
        self.track = []
        self.backtrack(nums, 0)
        return self.res
    def backtrack(self, nums, start):
        self.res.append(self.track[:])
        for i in range(start, len(nums)):
            self.track.append(nums[i])
            self.backtrack(nums, i+1)
            self.track.pop()

4.3 leetcode 77 组合

https://leetcode.cn/problems/combinations/description/
和子集非常类似,nums元素无重复,不可重复选

class Solution(object):
    def combine(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: List[List[int]]
        """
        self.res = []
        self.track = []
        self.backtrack(n, k, 1)
        return self.res
    def backtrack(self, n, k, start):
        if len(self.track) == k:
            self.res.append(self.track[:])
            return
        for i in range(start, n+1):
            self.track.append(i)
            self.backtrack(n, k, i+1)
            self.track.pop()
        return 

4.4 leetcode 90 子集II

https://leetcode.cn/problems/subsets-ii/description/
元素可重复,不可重复选取
这道题的思路很重要。以[1,2,2]为例,我们把第二个2叫做2’,画出决策树的图:
在这里插入图片描述

  • 可以看到用决策树中出现了重复,要进行剪枝。这里和子集I的区别在于,如果一个节点有多条值相同的树枝相邻,则只遍历第一条,剩下的都剪掉,不要去遍历。
  • 如何在代码中实现剪枝呢?首先要对nums排序,让相同元素都靠在一起。
  • 其次,在for循环遍历中,如果i > start且nums[i] == nums[i-1],则跳过。i > start很重要。i= start代表这是结点的第一个分支,无论如何不应该跳过。否则[2,2]和[1,2,2]就会被漏掉
class Solution(object):
    def subsetsWithDup(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        self.res = []
        self.track = []
        self.backtrack(nums, 0)
        return self.res
    def backtrack(self, nums, start):
        self.res.append(self.track[:])
        for i in range(start, len(nums)):
            if i > start and nums[i] == nums[i-1]: # i > start很重要。i= start代表这是结点的第一个分支,无论如何不应该跳过。否则[2,2]和[1,2,2]就会被漏掉
                continue
            self.track.append(nums[i])
            self.backtrack(nums, i+1)
            self.track.pop()
        return

4.5 leetcode40 组合总和

https://leetcode.cn/problems/combination-sum-ii/description/
上面子集II的变体,把res.append的条件稍微改一下即可。

class Solution(object):
    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        self.res = []
        self.track = []
        candidates.sort()
        self.backtrack(candidates, target, 0)
        return self.res
    def backtrack(self, candidates, target, start):
        if sum(self.track) == target:
            self.res.append(self.track[:])
            return
        for i in range(start, len(candidates)):
            if sum(self.track) + candidates[i] > target:
                continue
            if i > start and candidates[i] == candidates[i-1]:
                continue
            self.track.append(candidates[i])
            self.backtrack(candidates, target, i+1)
            self.track.pop()
        return                 

4.6 leetcode47 全排列II

https://leetcode.cn/problems/permutations-ii/description/
数组元素可重复
这里和全排列的框架一样,只是对数组排了序,并添加了一个剪枝的策略.重点在于我们如何剪枝?
标准全排列算法之所以出现重复,是因为把相同元素形成的排列序列视为不同的序列,但实际上它们应该是相同的;而如果固定相同元素形成的序列顺序,当然就避免了重复。
体现在代码上,就是我们先对nums排序,然后把2, 2’看做是有序的. 如果2没有在track中出现,那么2’就不应该添加到track中.

class Solution(object):
    def permuteUnique(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        self.res = []
        self.track = []
        self.used = [False] * len(nums)
        nums.sort()
        self.backtrack(nums)
        return self.res
    def backtrack(self, nums):
        if len(self.track) == len(nums):
            self.res.append(self.track[:])
            return
        for i, n in enumerate(nums):
            if self.used[i]:
                continue
            # 重点
            if i > 0 and nums[i] == nums[i-1] and not self.used[i-1]:
                continue
            self.used[i] = True
            self.track.append(n)
            self.backtrack(nums)
            self.track.pop()
            self.used[i] = False

4.7 leetcode39 组合总和

https://leetcode.cn/problems/combination-sum/
可重复选取数字
看起来麻烦,但实际上非常简单。
先想一下,不可重复选取数字的时候,我们是通过start = i + 1来控制不重复的。这里让start=i就可以重复选取了。easy!

class Solution(object):
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        self.res = []
        self.track = []
        self.backtrack(candidates, target, 0)
        return self.res
    def backtrack(self, nums, target, start):
        if sum(self.track) == target:
            self.res.append(self.track[:])
            return
        for i in range(start, len(nums)):
            if sum(self.track) + nums[i] > target:
                continue
            self.track.append(nums[i])
            self.backtrack(nums, target, i)
            self.track.pop()

4.8 leetcode没有,自己总结:可重复选取元素的全排列

和全排列类似,把used函数的控制去掉即可。

5. 总结:一个模板秒杀所有排列、组合、子集问题。

5.1 nums无重复,不可重复选取

#组合、子集问题
res, track  = [], []
def backtrack(nums, start):
    # 1. 退出条件
    if xxx:
        res.append(track[:])
        return
    # 2. 回溯框架
    for i in range(start, len(nums)):
        track.append(nums[i])
        backtrack(nums, i+1)
        track.pop()
#排列问题
res, track, used  = [], [], [False]*len(nums)
def backtrack(nums):
    # 1. 退出条件
    if xxx:
        res.append(track[:])
        return
    # 2. 回溯框架
    for i in range(len(nums)):
        if used[i]:
            continue
        used[i] = True
        track.append(nums[i])
        backtrack(nums)
        used[i] = False
        track.pop()

5.2 nums有重复,不可重复选取

  • 对nums排序
  • 剪枝:
    – 组合、子集:当i > start and nums[i] == nums[i-1]时剪枝
    – 排列:当i > 0 and nums[i] == nums[i-1] and !used[i-1]时剪枝
#组合、子集问题
res, track  = [], []
def backtrack(nums, start):
    # 1. 退出条件
    if xxx:
        res.append(track[:])
        return
    # 2. 回溯框架
    for i in range(start, len(nums)):
        if i > start and nums[i] == nums[i-1]
        track.append(nums[i])
        backtrack(nums, i+1)
        track.pop()
#排列问题
res, track, used  = [], [], [False]*len(nums)
def backtrack(nums):
    # 1. 退出条件
    if xxx:
        res.append(track[:])
        return
    # 2. 回溯框架
    for i in range(len(nums)):
        if used[i]:
            continue
        # 剪枝
        if i > 0 and nums[i] == nums[i-1] and not used[i-1]:
            continue
        used[i] = True
        track.append(nums[i])
        backtrack(nums)
        used[i] = False
        track.pop()

5.3 nums无重复,可重复选取

  • 组合、子集:递归时start = i
  • 排列:放飞自我,把used判断去掉
#组合、子集问题
res, track  = [], []
def backtrack(nums, start):
    # 1. 退出条件
    if xxx:
        res.append(track[:])
        return
    # 2. 回溯框架
    for i in range(start, len(nums)):
        if i > start and nums[i] == nums[i-1]
        track.append(nums[i])
        backtrack(nums, i)
        track.pop()
#排列问题
res, track  = [], []
def backtrack(nums):
    # 1. 退出条件
    if xxx:
        res.append(track[:])
        return
    # 2. 回溯框架
    for i in range(len(nums)):
        track.append(nums[i])
        backtrack(nums)
        track.pop()

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

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

相关文章

个人 AI 的革命:Nvidia‘s Chat with RTX 深度探索

个人 AI 的革命&#xff1a;Nvidias Chat with RTX 深度探索 Nvidia 推出的 Chat with RTX 预示着个人 AI 新时代的到来。2 月 13 日&#xff0c;Nvidia 官宣了自家的 AI 聊天机器人&#xff0c;这不仅是人工智能交互的渐进式改进&#xff1b;更代表了个人如何利用自己的数据进…

Dirty PageTable

前言 Dirty PageTable 是一种针对堆相关漏洞的利用手法&#xff0c;主要就是针对 PTE 进行攻击。 参考文章&#xff1a; Dirty Pagetable: A Novel Exploitation Technique To Rule Linux Kernel – 该利用方式提出原文 上述文章已经讲的非常清楚了&#xff0c;就是实操写 e…

25天物理探索旅程 - 第四天:光的奇妙旅程揭秘

第四天&#xff0c;我们的科普探险队将踏上一段非凡的旅程&#xff0c;目标是揭开光——这位宇宙间最具魔法特质的信使的秘密面纱。今天&#xff0c;我们将以一种轻松愉快、幽默风趣的方式探讨光的本质&#xff0c;像看一场生动有趣的魔术表演般&#xff0c;领略光那波粒二象性…

Java基础常见面试题总结-并发(一)

线程池 线程池&#xff1a;一个管理线程的池子。 为什么平时都是使用线程池创建线程&#xff0c;直接new一个线程不好吗&#xff1f; 嗯&#xff0c;手动创建线程有两个缺点 不受控风险频繁创建开销大 为什么不受控&#xff1f; 系统资源有限&#xff0c;每个人针对不同业…

垃圾分类|城市垃圾分类管理系统|基于Springboot的城市垃圾分类管理系统设计与实现(源码+数据库+文档)

城市垃圾分类管理系统目录 目录 基于Springboot的城市垃圾分类管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、垃圾列表 2、公告信息管理 3、公告类型管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 …

thinkphp+vue企业产品展示网站f7enu

本文首先介绍了企业产品展示网站管理技术的发展背景与发展现状&#xff0c;然后遵循软件常规开发流程&#xff0c;首先针对系统选取适用的语言和开发平台&#xff0c;根据需求分析制定模块并设计数据库结构&#xff0c;再根据系统总体功能模块的设计绘制系统的功能模块图&#…

qml之Control类型布局讲解,padding属性和Inset属性细讲

1、Control布局图 2、如何理解&#xff1f; *padding和*Inset参数如何理解呢&#xff1f; //main.qml import QtQuick 2.0 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import QtQuick.Controls 1.4 import QtQml 2.12ApplicationWindow {id: windowvisible: …

CentOS7.9+Kubernetes1.29.2+Docker25.0.3高可用集群二进制部署

CentOS7.9Kubernetes1.29.2Docker25.0.3高可用集群二进制部署 Kubernetes高可用集群&#xff08;Kubernetes1.29.2Docker25.0.3&#xff09;二进制部署二进制软件部署flannel v0.22.3网络&#xff0c;使用的etcd是版本3&#xff0c;与之前使用版本2不同。查看官方文档进行了解…

无人机导航技术,无人机导航理论基础,无人机导航技术应用发展详解

惯性/卫星定位组合是一种比较理想的组合导航系统。在无人机导航领域&#xff0c;多年来惯性/卫星定位组合导航系统的研究一直受到普遍的关注&#xff0c;大量的理论研究成果得到实际应用。 常见的几类导航系统 单一导航 卫星导航系统 、多普勒导航、惯性导航系统(INS) 、图形…

苹果展示 AI 新模型 MGIE,可一句话精修图片

苹果公司近日发布了名为“MGIE”的新型开源人工智能模型&#xff0c;它可以根据自然语言指令编辑图像。 2 月 8 日消息&#xff0c;相比较微软的风生水起&#xff0c;苹果公司在 AI 领域的布局显得低调很多&#xff0c;但这并不意味着苹果在该领域就没有丝毫建树。苹果公司近日…

Unresolved reference: kotlinx 和 Unresolved reference:xxx

Unresolved reference: kotlinx 这个报错是因为build.gradle中忘记apply plugin了 apply plugin: kotlin-android-extensions如下 同步以后再次编译发现报错 Unresolved reference:xxx 是因为用于使用 Gradle 构建的 Kotlin 版本与 IDE 插件中的版本不一样的原因 解决方法 …

Lag-Llama:第一个时间序列预测的开源基础模型介绍和性能测试

2023年10月&#xff0c;我们发表了一篇关于TimeGPT的文章&#xff0c;TimeGPT是时间序列预测的第一个基础模型之一&#xff0c;具有零样本推理、异常检测和共形预测能力。 虽然TimeGPT是一个专有模型&#xff0c;只能通过API访问。但是它还是引发了对时间序列基础模型的更多研…

算法刷题:有效三角形个数

有效三角形个数 .题目链接题目详情算法原理补充知识点双指针:对撞指针 我的答案 . 题目链接 有效三角形个数 题目详情 算法原理 补充知识点 有效三角形需要满足的条件: ab>cac>bbc>a 其实在满足1的时候,c是最大的,那么2和3是显然成立的,因此我们可以这样解题: 对…

C# winfrom中NPOI操作EXCEL

前言 1.整个Excel表格叫做工作表&#xff1a;WorkBook&#xff08;工作薄&#xff09;&#xff0c;包含的叫页&#xff08;工作表&#xff09;&#xff1a;Sheet&#xff1b;行&#xff1a;Row&#xff1b;单元格Cell。 2.忘了告诉大家npoi是做什么的了&#xff0c;npoi 能够读…

react 【七】各种hooks的使用/SPA的缺点

文章目录 1、Hook1.1 为什么会出现hook1.2 useState1.3 useEffect1.4 useContext1.5 useReducer1.6 useCallback1.7 useMemo1.8 useRef1.8.1 ref绑定dom1.8.2 ref解决闭包缺陷 1.9 useImperativeHandle1.10 useLayoutEffect1.11 自定义Hook1.11.1 什么是自定义Hook1.11.2 Conte…

Python 字符串格式化输出

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站零基础入门的AI学习网站~。 前言 字符串格式化是编程中一个常见的需求&#xff0c;它可以们将不同类型的数据&#xff08;如数字、文本、日…

Django问题报错:TypeError: as_view() takes 1 positional argument but 2 were given

一、错误位置 from django.urls import pathfrom users_app.views import RegisterView, LoginView, LogoutViewapp_name users urlpatterns [path("register/", RegisterView.as_view, name"register"),path("login/", LoginView.as_view, n…

基于四叉树的图像分割算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ........................................................... Imgs(dx 1 : dx R1, dy 1 …

阿里云幻兽帕鲁Linux 服务器下载游戏存档的方法

阿里云幻兽帕鲁Linux 服务器下载游戏存档的方法也非常简单。 远程连接到阿里云的 linux服务器后&#xff0c;可以在 ECS 远程连接命令行界面&#xff0c;点击左上角的文件&#xff0c;打开文件树。通过一行命令打包。 在打包后的 Saved.tar 文件上右键&#xff0c;选择 下载文…

【Go语言】Go项目工程管理

GO 项目工程管理&#xff08;Go Modules&#xff09; Go 1.11 版本开始&#xff0c;官方提供了 Go Modules 进行项目管理&#xff0c;Go 1.13开始&#xff0c;Go项目默认使用 Go Modules 进行项目管理。 使用 Go Modules的好处是不再需要依赖 GOPATH&#xff0c;可以在任意位…