本期任务:介绍算法中关于回溯思想的几个经典问题
一、问题描述
问题来源:LeetCode 78. 子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
二、算法思路
- 使用回溯思想,暴力穷举,在示例中,每一个位置都可能有1,2,3,[](空集)共4种可能,所有可能的摆放方式共有 ,穷举过程遵循深度优先搜索规则。
- 剪枝策略:只遍历当前数字之后的数字或空集。
- 结算情形:进行了三轮递归时
以nums=[1, 2]为例,递归树如下:
三、Python代码实现
class Solution:
def subsets(self, nums):
self.nums = nums
self.size = len(self.nums)
self.ans = []
self.nums.append([]) # 增加一个空集,保证每个叶子节点都递归self.size层
self.helper([], 0)
return self.ans
def helper(self, arr, index):
if index == self.size: # 总共进行n轮
# 对结果做排序和去重
arr.sort()
if arr not in self.ans:
self.ans.append(arr)
return
for v in self.nums[index:]: # 每次可选择的有self.size-index种(包含空集)
if v not in arr:
self.helper(arr + [v] if v != [] else arr, index + 1)
def main():
client = Solution()
print(client.subsets([1, 2, 3]))
if __name__ == '__main__':
main()
输出结果:
[[1, 2, 3], [1, 2], [1, 3], [1], [2, 3], [2], [3], []]
四、问题变形
问题来源:LeetCode 90. 子集 II
- 题目描述
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
-
解决方案:维护一个existed数组,记录各个位置的被访问情况,同时使用集合existed_f来记录访问过的状态进一步剪枝。
-
具体代码
class Solution:
def subsetsWithDup(self, nums):
self.nums = nums
self.size = len(self.nums)
self.ans = []
self.nums.append([]) # 增加一个空集,保证每个叶子节点都递归self.size层
self.existed = [0] * (self.size + 1)
self.existed_f = set() # 使用集合存储已计算过的子问题
self.helper([], 0)
return self.ans
def helper(self, arr, index):
if index == self.size: # 总共进行n轮
# 对结果做排序和去重
arr.sort()
if arr not in self.ans:
self.ans.append(arr)
return
for i, v in enumerate(self.nums): # 每次可选择的有self.size-index种(包含空集)
if i >= index and self.existed[i] == 0 or i == self.size: # 空集可以多次使用
new_arr, new_index = arr + [v] if v != [] else arr, index + 1
new_arr.sort()
new_arr1 = tuple(new_arr)
if (new_arr1, new_index) not in self.existed_f:
self.existed_f.add((new_arr1, new_index))
self.existed[i] = 1
self.helper(new_arr, new_index)
self.existed[i] = 0
def main():
client = Solution()
print(client.subsetsWithDup([1, 4, 3, 5, 4, 4, 7, 7, 8, 0]))
if __name__ == '__main__':
main()