一、简介
回溯法(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