## 46. 全排列
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1]
输出:[[1]]
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums
中的所有整数 互不相同
状态树法
这道题目中状态树做法和深搜做法是一模一样的
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
s = [False] * n # 用于标记是否已使用过该位置的数
path = [] # 当前构建的排列
ans = [] # 存储所有的排列
def dfs(iterator):
if iterator == n: # 完成一次排列
ans.append(path.copy()) # 将当前排列加入到结果集中
return
for i in range(n):
if not s[i]: # 如果第i个数还没有被使用
s[i] = True # 标记为已使用
path.append(nums[i]) # 将该数添加到当前排列
dfs(iterator + 1) # 递归调用以处理下一个位置
{{path.pop() # 回溯,撤销上一步的选择}}
s[i] = False # 取消对该数的使用标记
dfs(0)
return ans
深度搜索法
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
ans = []
path = []
used = [False for _ in range(n)]
def dfs(iterator):
if iterator == n:
ans.append(path.copy())
return
for i in range(n):
if not used[i]:
used[i] = True
path.append(nums[i])
dfs(iterator+1)
path.pop()
used[i] = False
dfs(0)
return ans
77. 组合
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
示例 2:
输入:n = 1, k = 1
输出:[[1]]
提示:
1 <= n <= 20
1 <= k <= n
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
if n == 0 or k > n:
return []
elif n == k:
return [list(range(1,n+1))]
# 回溯函数(组合类型)
# params:待挑选数组长度
# params:每个组合需要的数组长度
# params:待挑选的数组开始索引(即迭代器iterator,用迭代器普适性更强)
# params:挑选过程中的路径path
def backtracking(n,k,begin,path):
if len(path) == k:
ans.append(path.copy())
return
for i in range(begin,n+1):
path.append(i)
# 注意传递的是i+1,而不是begin+1
# 意思就是从当前挑选的数往后挑选,不会回头看
# begin表示的不是当前的数,而是这一轮dfs开始的数
backtracking(n,k,i+1,path)
path.pop()
ans = []
backtracking(n,k,1,[])
return ans
78. 子集
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums
中的所有元素 互不相同
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
# 每次选择拿不拿,迭代器一直往前走,迭代器结束时将path加入ans并return
ans = []
path = []
# 回溯函数(子集类型)
# params:待挑选的集合
# params:迭代器iterator
# params:挑选过程中的路径path
def backtracking(nums,iterator,path):
# 当iterator到底时加入path
if iterator == len(nums):
ans.append(path.copy())
return
# 加入当前iterator的数
path.append(nums[iterator])
backtracking(nums,iterator+1,path)
path.pop()
# backtracking(nums,iterator+1,path+[nums[iterator]])
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>此处为一个右值副本,不是左值所以不是按引用传递
# 不加入当前iterator的数
backtracking(nums,iterator+1,path)
# >>>>>>>>>>>>>>>>>>>>>>>>>>>这里是左值,所以是按引用传递
backtracking(nums,0,[])
return ans
90. 子集 II
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的 子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
# 首先进行排序获得以便利用顺序特性:该题中的特性为,按顺序来取的话子集中也只会是按顺序排列,那么元组就会自动过滤相同的子集,如果不排序,cnt相同的元组可能会因为顺序不同不能过滤掉,所以要先进行排列
nums.sort()
# 该题与子集I的不同点就在于需要去重:
# 1.利用set过滤不同的元组(之所以是元组(tuple)是因为列表(list)不具有hash性不能加入set)
# 2.利用排序过滤cnt相同而顺序不同的数组
ans_set = set()
path = []
def backtracking(nums, iterator, path):
if iterator == len(nums):
ans_set.add(tuple(path))
return
backtracking(nums,iterator+1,path)
backtracking(nums,iterator+1,path+[nums[iterator]])
backtracking(nums,0,[])
return list(ans_set)