我们先接着做这个题:
练习
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
这个意思就是对于顺序不同的也会是不同的答案
我直接写代码 这和我第一个关于回溯的博客是重合的 因为元素是不可以被重复使用的 所以会设置一个used数组 如果这个元素已经被使用过 那就被剪枝 不然的话就可以进栈 然后递归 直到return之后回来 然后再尝试其他的情况
class Solution(object): def permute(self, nums): res = [] n = len(nums) def backtrack(path,used): if len(path) == n: # 检查当前路径的和 res.append(path[:]) # 记录合法组合 return # 剪枝条件这个元素是没有用过的 如果这个元素用过那就不可以再用 for i in range(n): if not used[i]: path.append(nums[i]) used[i]=True backtrack(path, used) path.pop() used[i]=False backtrack([],[False]*n) return res solution=Solution() result=solution.permute([1,2,3]) print(result)
知识点
这个代码应该没什么说的 都是常规情况 那么为什么这里一定要使用used数组呢?因为在这里的是排列 是允许不同的顺序的 为了避免大家晕 我这里将区别列出
组合:就拿使得总和等于3为例子,顺序不重要 [1,2]和[2,1]是同一个组合 如何去重呢?一般是先对数组进行排序 然后使用i>start和相邻元素相等两个条件来跳过同一层的相等的元素 意思是这个元素只能在其他层 充当这个元素的辅助 配角 不能自己再单开了 单开的资格已经被前面和它相等的元素拿走了 递归的话就是backtrack(i+1,path)既不重复选择当前的元素 也不会回头选 (注意要结合题意 是不是当前元素可以重复选 我这个说的是之前的博客的一道题目)
排列:顺序很重要,不同的顺序就是不同的排列。在初中大概就学过这样的。你想啊 个子高的站前面和站后面这个肯定排列是不一样的 对于排列而言 是不能出现已经使用过的元素使用第二遍的 所以要使用used数组去标记已经使用的元素 避免重复选择同一个元素 backtrack(path)
+ used
(允许任意顺序,但不能重复选)
大家要注意二者的区别是什么 组合因为不允许重复组合 所以排序之后 “不回头选” 是 start
实现的(例如 backtrack(i+1, ...)
),但去重的核心是跳过同一层的重复数字(在同一层的相同的元素 i > start
的含义:当前数字不是这一层的第一个选择(即不是 start
指向的位置)所以是跳过同一层) 而对于排列来说 顺序是很重要的 所以元素任意顺序 但是我们要使用used标记已经使用的元素 避免元素重复(刚刚那个题是不包括重复元素的)
一些误区:
1.在排列中使用start
start是使得已经选择的元素不能再单开 控制下一层递归的起始位置(即“不回头选”)去重的话是通过相邻元素相等这样的条件进行控制的 start
防止“回头选”更小的数字(如 [2,1]
被避免,因为 [1,2]
已覆盖) 但是在排列中 这是两个不同的排列 所以这样的话就不符合排列的规则了 不要在排列当中使用start
2.在组合中不使用start
如果在组合中不使用start 会导致已经当配角的元素 下一次又一次当头了 生成两个相同的组合
关键点就是要明白i > start
仅表示:当前数字不是这一层的第一个选择(即 i
不是 start
)但是它无法判断重复 要是想判断重复 必须结合 nums[i] == nums[i-1]
码字不易 如果你喜欢这篇文章 欢迎点赞收藏