首页 > 编程语言 >理解回溯算法——从全排列问题开始

理解回溯算法——从全排列问题开始

时间:2023-04-08 21:44:48浏览次数:45  
标签:从全 结点 排列 res 算法 回溯 path size

一、简介

回溯法(backtracking)是优先搜索的一种特殊情况,又称为试探法,常用于需要记录节点状
态的深度优先搜索。通常来说,排列、组合、选择类问题使用回溯法比较方便。

 

二、从全排列问题开始理解回溯算法
以数组 [1, 2, 3] 的全排列为例。

先写以 1开头的全排列,它们是:[1, 2, 3], [1, 3, 2],即 1 + [2, 3] 的全排列(注意:递归结构体现在这里);
再写以 2 开头的全排列,它们是:[2, 1, 3], [2, 3, 1],即 2 + [1, 3] 的全排列;
最后写以 3 开头的全排列,它们是:[3, 1, 2], [3, 2, 1],即 3 + [1, 2] 的全排列。
总结搜索的方法:按顺序枚举每一位可能出现的情况,已经选择的数字在 当前 要选择的数字中不能出现。按照这种策略搜索就能够做到 不重不漏。这样的思路,可以用一个树形结构表示。

看到这里的朋友,建议先尝试自己画出「全排列」问题的树形结构。

 

 

 

说明:

1、每一个结点表示了求解全排列问题的不同的阶段,这些阶段通过变量的「不同的值」体现,这些变量的不同的值,称之为「状态」;
2、使用深度优先遍历有「回头」的过程,在「回头」以后, 状态变量需要设置成为和先前一样 ,因此在回到上一层结点的过程中,需要撤销上一次的选择,这个操作称之为「状态重置」;
3、深度优先遍历,借助系统栈空间,保存所需要的状态变量,在编码中只需要注意遍历到相应的结点的时候,状态变量的值是正确的,具体的做法是:往下走一层的时候,path 变量在尾部追加,而往回走的时候,需要撤销上一次的选择,也是在尾部操作,因此 path 变量是一个栈;
4、深度优先遍历通过「回溯」操作,实现了全局使用一份状态变量的效果。
使用编程的方法得到全排列,就是在这样的一个树形结构中完成 遍历,从树的根结点到叶子结点形成的路径就是其中一个全排列。

 

三、题解

46. 全排列

 1 class Solution:
 2     def permute(self, nums: List[int]) -> List[List[int]]:
 3         def dfs(nums, size, depth, path, used, res):
 4             if depth == size:
 5                 res.append(path[:])  # 注意append方法是浅拷贝,如果用res.append(path)是不行的,最后输出的都是[]
 6                 return
 7 
 8             # 在非叶子结点处,产生不同的分支,这一操作的语义是:在还未选择的数中依次选择一个元素作为下一个位置的元素,这显然得通过一个循环实现。
 9             for i in range(size):
10                 if not used[i]:
11                     used[i] = True
12                     path.append(nums[i])
13 
14                     dfs(nums, size, depth + 1, path, used, res)
15 
16                     # 注意:下面这两行代码发生 「回溯」(需要做「状态重置」,即「回到过去」)
17                     # 回溯发生在从 深层结点 回到 浅层结点 的过程,代码在形式上和递归之前是对称的
18                     used[i] = False
19                     path.pop()
20 
21         size = len(nums)
22         if len(nums) == 0:
23             return []
24 
25         used = [False for _ in range(size)]
26         res = []
27         dfs(nums, size, 0, [], used, res)
28         return res

 

标签:从全,结点,排列,res,算法,回溯,path,size
From: https://www.cnblogs.com/spacerunnerZ/p/17299301.html

相关文章

  • 【算法数据结构专题】「延时队列算法」史上手把手教你针对层级时间轮(TimingWheel)实现
    承接上文承接上一篇文章【算法数据结构专题】「延时队列算法」史上手把手教你针对层级时间轮(TimingWheel)实现延时队列的开发实战落地(上)】我们基本上对层级时间轮算法的基本原理有了一定的认识,本章节就从落地的角度进行分析和介绍如何通过Java进行实现一个属于我们自己的时间轮服务......
  • HJ67_24点游戏算法_多维递归_DFS(深度优先搜索)
    思路:多维递归,深度有限遍历加减乘除四种情况。知识点:1、多维递归不能对传递的变量进行修改,否则无法回溯。应该传递一个新地址的变量,如代码所示,传递切片的列表,不修改列表  2、搜索遗漏。两括号比如((9-4)-1)*6选取任意一个数作为第一个运算数与24运算,不能找出所有24点的计算......
  • Tarjan 算法学习笔记
    (绝大部分都是贺的,来自OI-WIKI和洛谷题解,自己抄一遍印象深刻一点,部分代码未编译,不保证正确性,但大体是对的)一、DFS生成树注意可能有多棵,因为图可能不联通。树边(treeedge):示意图中以黑色边表示,每次搜索找到一个还没有访问过的结点的时候就形成了一条树边。反祖边(backedge):......
  • Python 进阶指南(编程轻松进阶):十三、性能测量和大 O 算法分析
    原文:http://inventwithpython.com/beyond/chapter13.html对于大多数小程序来说,性能并不那么重要。我们可能会花一个小时编写一个脚本来自动执行一个只需要几秒钟就能运行的任务。即使需要更长的时间,当我们端着一杯咖啡回到办公桌时,这个项目也可能已经完成了。有时候花时间学......
  • Chapter2 K-近邻算法案例1
    案例2:使用K-近邻算法实现手写数字系统1.案例要求编写一个程序,应用K-近邻算法,实现手写数字系统。通过画图生成一个32*32的数字图像,再将图像转化为代表数字的0-1文本文件。之后往程序输入代表数字的0-1文本文件,程序便可以输出相应的数字。2.案例的执行流程示例......
  • 【生产调度】基于和声搜索算法实现并行机器调度附matlab代码
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 算法C#
    #region二分查找法publicstaticintBinarySertch(int[]arr,intstartIndex,intendIndex,intresult){if(startIndex>endIndex){return-1;}intmidIndex=(end......
  • 09、OpenFoam中的PISO,SIMPLE和PIMPLE算法
    隐式:PISO半隐式:SIMPLE组合式:PIMPLE(PISO+SIMPLE)PISO算法PISO算法是一种常用于求解不可压缩流体流动问题的数值方法,它在OpenFOAM中被广泛应用。PISO算法的全称为PressureImplicitwithSplittingofOperators,即利用算子分裂的方法进行隐式求解压力和速度。PISO算法主要分为......
  • 递归-回溯
    Stack<int>stack=newStack<int>();inttemp=0;HS(92);voidHS(inti){if(i==100)return;stack.Push(i);Console.WriteLine($"回溯前i:{i}");HS(i+1);temp=stack.Pop();Console.WriteLine($"回溯后i:{temp......
  • Chapter2 K-近邻算法案例
    案例1:使用K-近邻算法分类爱情片和动作片1.案例要求创建一个应用,应用K-近邻算法,将样本分到以下三种类别。1.不喜欢的人2.魅力一般的人3.极具魅力的人2.案例的执行流程示例:在约会网站上使用k-近邻算法(1)收集数据:提供文本文件......