前言
- 上课听课不认真,简单听了一下回溯法的基本思想,照葫芦画瓢的写了下面的解法,结果很不理想。
- 因此写下此博客以自省。
题目
给你一个整数数组 nums ,其中不包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
- 示例 1:
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
- 示例2:
输入:nums = [0]
输出:[[],[0]]
我的思路
- 我的思想是先根据列表的长度产生所有对应长度的二进制数。
- 然后根据二进制的1,0来决定列表对应位置的数的取与不取。
- 最后将所有的列表整合到一个列表中。
- 这样做最后的结果很不理想,时间复杂度和空间复杂度都很高,来看看真正的回溯法改怎么写。
真正的回溯法
- 未探索区域:剩余的未搜索的数组 nums[index: N - 1] ;
- 每个 path 是否满足题目的条件: 任何一个 path 都是子集,都满足条件,都要放到 res 中 ;
- 当前 path 满足条件时,是否继续搜索:是的,找到 nums[0:index-1] 中的子集之后, nums[index] 添加到老的 path 中会形成新的子集。
- 未探索区域当前可能的选择:每次选择可以选取 s 的 1 个字符,即 nums[index] ;
- 当前选择符合要求:任何 nums[index] 都是符合要求的,直接放到 path 中;
- 新的未探索区域:nums 在 index 之后的剩余字符串, nums[index + 1 : N - 1] 。
- 回溯法的整体思路是:搜索每一条路,每次回溯是对具体的一条路径而言的。对当前搜索路径下的的未探索区域进行搜索,则可能有两种情况:
- 当前未搜索区域满足结束条件,则保存当前路径并退出当前搜索;
- 当前未搜索区域需要继续搜索,则遍历当前所有可能的选择:如果该选择符合要求,则把当前选择加入当前的搜索路径中,并继续搜索新的未探索区域。
回溯法搜索可行解的模板
res = []
path = []
def backtrack(未探索区域, res, path):
if path 满足条件:
res.add(path) # 深度拷贝
# return # 如果不用继续搜索需要 return
for 选择 in 未探索区域当前可能的选择:
if 当前选择符合要求:
path.add(当前选择)
backtrack(新的未探索区域, res, path)
path.pop()
答案
- 我的“回溯法”
import numpy as np
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
data = np.array(nums)
data = np.unique(data)
pre = np.power(2,len(data))
Rres = []
for i in range(0,pre):
res = []
tree = []
num = len(data)+2-len(bin(i))
while(num> 0):
tree.append('0')
num-=1
for j in range(2,len(data)+2,1):
try:
tree.append(bin(i)[j])
except IndexError:
continue
for i in range(0,len(data)):
if tree[i] == '1':
res.append(int(data[i]))
res.sort()
if res not in Rres:
Rres.append(res)
return Rres
- 大佬的回溯法
class Solution(object):
def subsets(self, nums):
res, path = [], []
self.dfs(nums, 0, res, path)
return res
def dfs(self, nums, index, res, path):
res.append(copy.deepcopy(path))
for i in range(index, len(nums)):
path.append(nums[i])
self.dfs(nums, i + 1, res, path)
path.pop()
后记
- 此文章为个人力扣题目小记
- 部分内容摘自力扣题解部分,但摘的大部分是我个人力扣的题解,如其他地方有侵权,请联系删除。