DFS算法模板:
def dfs(array or root, cur_layer, path, result):
if cur_layer == len(array) or not root:
result.append(path)
return
for i in range(cur_layer, len(array)):
do something with array[cur_layer:i+1] or nodes with this layer
path.append(xxx) # 修改path 或者 其他操作
dfs(array, i + 1 or cur_layer + 1, path, result)
path.pop() # 还原path 或者 还原之前的操作
排列组合是组合学最基本的概念。所谓排列,就是指从给定个数的元素中取出指定个数的元素进行排序。组合则是指从给定个数的元素中仅仅取出指定个数的元素,不考虑排序。
https://baike.baidu.com/item/排列组合/706498
(一)组合
//组合
def recursive_c(cur, m, cur_list, original_list, result_list):
if m == 0:
result_list.append(list(cur_list))
return
for i in range(cur, len(original_list)):
cur_list.append(original_list[i])
recursive_c(i + 1, m - 1, cur_list, original_list, result_list)
cur_list.pop()
>>original_list = [1, 2, 3, 4]
>>result_list =[[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
>>共4种
【 题目描述】给定一个数组 num (整数)和一个整数 target. 找到 num 中所有的数字之和为 target 的组合.
原文链接:https://blog.csdn.net/qq_19446965/article/details/81775702
/查找和为target的所有组合,DFS法
def dfs(cur, sum, target, path_list, original_list, result_list):
if sum == target:
result_list.append(list(path_list))
return
if sum > target:
return
for i in range(cur, len(original_list)):
path_list.append(original_list[i])
dfs(i + 1, sum + original_list[i], target, path_list, original_list, result_list)
path_list.pop()
【题目描述】给定一个数组 num 和一个整数 target. 找到 num 中所有的数字(不重复)之和为 target 的组合.
原文链接:https://www.jiuzhang.com/solutions/combination-sum-ii/
注意:if i > 0 and original_list[i] == original_list[i-1]:
def dfs(cur, sum, target, path_list, original_list, result_list):
if target == sum:
result_list.append(path_list)
return
if sum > target:
return
for i in range(cur, len(original_list)):
if i > 0 and original_list[i] == original_list[i-1]:
continue
dfs(i + 1, sum + original_list[i], target, path_list + [original_list[i]], original_list, result_list)
【题目描述】给定一个数组 num 和一个整数 target. 找到 num 中所有的数字之和为 target 的组合(不重复).
https://www.jiuzhang.com/solutions/combination-sum-ii/
注意:if i > cur and original_list[i] == original_list[i-1]:
def dfs(cur, sum, target, path_list, original_list, result_list):
if target == sum:
result_list.append(path_list)
return
if sum > target:
return
for i in range(cur, len(original_list)):
if i > cur and original_list[i] == original_list[i-1]:
continue
dfs(i + 1, sum + original_list[i], target, path_list + [original_list[i]], original_list, result_list)
【题目描述】
给定一个候选数字的集合 candidates 和一个目标值 target. 找到 candidates 中所有的和为 target 的组合.
在同一个组合中, candidates 中的某个数字不限次数地出现.
https://www.jiuzhang.com/solutions/combination-sum/
分析:
Combination Sum 一个数可以选很多次,搜索时从 index 开始而不是从 index + 1
dfs(candidates, index, target - candidates[index], combination, results);
(二)排列
问题模型:求出所有满足条件的“排列”。
判断条件:组合中的元素是顺序“相关”的。
时间复杂度:与 n! 相关。
//全排列
def recursive_a(m, cur_list, original_list, result_list):
if m == 0:
result_list.append(list(cur_list))
return
for i in range(len(original_list)):
cur_list.append(original_list[i])
temp_list = deepcopy(original_list)
temp_list.pop(i)
recursive_a(m - 1, cur_list, temp_list, result_list)
cur_list.pop()
>>original_list = [1, 2, 3, 4]
>>result_list = [[1, 2, 3], [1, 2, 4], [1, 3, 2], [1, 3, 4], [1, 4, 2], [1, 4, 3], [2, 1, 3], [2, 1, 4], [2, 3, 1], [2, 3, 4], [2, 4, 1], [2, 4, 3], [3, 1, 2], [3, 1, 4], [3, 2, 1], [3, 2, 4], [3, 4, 1], [3, 4, 2], [4, 1, 2], [4, 1, 3], [4, 2, 1], [4, 2, 3], [4, 3, 1], [4, 3, 2]]
>>共24种
https://blog.csdn.net/qq_19446965/article/details/102463792
【题目】给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations-ii
方法一:先生成再去重
def helper(self, nums, res, path):
if not nums and path not in res:
res.append(path)
else:
for i in range(len(nums)):
self.helper(nums[:i] + nums[i+1:], res, path + [nums[i]])
耗时:1356 ms
这样的做法是在每个path生成之后才去做的判断,因此效率一点都不高。
方法二:深度拷贝
def helper(self, nums, res, path):
if not nums and path not in res:
res.append(list(path))
return
for i in range(len(nums)):
path.append(nums[i])
temp_list = deepcopy(nums)
temp_list.pop(i)
self.helper(temp_list, res, path)
path.pop()
耗时:超时
方法三:回溯法
下面这个做法是标准的回溯法,需要用到visited来表示哪些位置已经被添加到path中了。
为什么有重复?
在这个例子中,我们在第一个1开始的排列中已经取了第二个1的情况;如果在第二个1开始的排列中仍然取第一个1,就有重复了。
如何去重呢?
1)排序
2)不是第一个数字,并且现在的数字和前面的数字相等,同时前面的数字还没有访问过,我们是不能搜索的,需要直接返回。
原因是,这种情况下,必须是由前面搜索到现在的这个位置,而不能是由现在的位置向前面搜索。
def helper(self, nums, res, val, visit):
if len(val) == len(nums) :
res.append(val)
for i in range(len(nums)):
if i > 0 and not visit[i - 1] and nums[i - 1] == nums[i]:
continue
if not visit[i]:
visit[i] = True
self.helper(nums, res, val+[nums[i]], visit)
visit[i] = False
耗时:56 ms
方法四:交换思想
def swap(nums, i, cur):
nums[cur], nums[i] = nums[i], nums[cur]
def helper_1(cur, nums, res):
if cur == len(nums):
res.append(list(nums))
for i in range(cur, len(nums)):
if nums[i] not in nums[i+1:]:
swap(nums, i, cur)
helper(cur + 1, nums, res)
swap(nums, i, cur)
耗时:44 ms