一个集合的所有子集从空集开始,一直到自己本身,都是其子集
leetcode 78子集
没有重复元素集合的子集
同样的,套回溯模板
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
if not nums:
return [[]]
n = len(nums)
res = []
def backtrace(start, tmp):
#所有情况都是满足要求的,所以直接保存结果
res.append(tmp[:])
for i in range(start, n):
#这里没有使用状态数组的原因是,我们每次回溯遍历的起点都不一样
#也就是说,根本就不会发生再次选择前面元素的情况
#做选择
tmp.append(nums[i])
backtrace(i+1, tmp)
#撤销选择
tmp.pop()
backtrace(0, [])
return res
子集 II
那么有重复元素的集合子集呢?
同样的,对于有重复元素的,先对数组进行排序,这根处理全排列是一样的;第二个相同的,也需要一个状态数组来记录元素使用情况,主要是激励相同元素的使用情况,避免出现重复的子集。
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
if not nums:
return [[]]
nums.sort()
n = len(nums)
res = []
#count = 0
status = [False] * n
def backtrace(start, tmp):
res.append(tmp[:])
#这部分代码和有重复元素的全排列代码基本一样,唯一不同的是,每次的遍历起点不一样
for i in range(start, n):
if not status[i]:
if i > 0 and nums[i] == nums[i-1] and not status[i-1]:
continue
status[i] = True
tmp.append(nums[i])
backtrace(i+1, tmp)
tmp.pop()
status[i] = False
backtrace(0, [])
return res
这两道题,包括全排列的那两道,整体代码是不是很像,就是套回溯模板,如出一辙。
leetcode 1079 活字印刷
综合了全排列和子集的一道题
思路:先求出原字符串的所有子集,因为有重复的元素,处理方式如上;然后再求出每一个子集的所有全排列,求和即为最终结果。就是先求子集,再求全排列。这两部分代码,基本上不需要改动,增加一个计数的变量即可
class Solution:
def numTilePossibilities(self, tiles: str) -> int:
if not tiles:
return 0
tiles = list(tiles)
tiles.sort()
n = len(tiles)
res1 = []
status1 = [False] * n
#求有重复元素集合的子集,完全不用改动
def backtrace1(start, tmp):
res1.append(tmp[:])
for i in range(start, n):
if not status1[i]:
if i > 0 and tiles[i] == tiles[i-1] and not status1[i-1]:
continue
status1[i] = True
tmp.append(tiles[i])
backtrace1(i+1, tmp)
tmp.pop()
status1[i] = False
#求全排列
count = 0
def backtrace2(res, tmp, length):
nonlocal count
if len(tmp) == length:
count += 1
#res2.append(tmp[:])
return
for i in range(length):
if not status2[i]:
if i > 0 and res[i] == res[i-1] and not status2[i-1]:
continue
status2[i] = True
tmp.append(res[i])
backtrace2(res, tmp, length)
tmp.pop()
status2[i] = False
backtrace1(0, [])
#第一个子集是空集,所以从1开始
for i in range(1, len(res1)):
length = len(res1[i])
status2 = [False] * length
backtrace2(res1[i], [], length)
return count
类似的求子集的题目还有
39.组合总和
40.组合总和II
同样是分有无重复元素,有重复元素的就用状态数组记录一下,没有就不用