491.递增子序列(medium)
-
自己思路:
-
树形图画出后显示,思路与#78和#90子集问题类似。同样也需要收集所有节点的组合(因此收集这一步需要放在终止条件前,否则只是收集叶子节点的组合),但是题目要求长度为1的组合不要,可以通过判断组合长度来决定是否收集(组合长度大于1才可以)。
-
在收集节点组合时,判断是否为升序(if self.path == sorted(self.path))
-
去重
-
class Solution:
def __init__(self):
self.path = []
self.result = []
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
return self.backtracking(nums, 0)
def backtracking(self, nums, startIndex):#step1
#step2
if nums is None:
return []
if len(self.path) > 1 and self.path == sorted(self.path):
#树形图显示也是要收集所有节点的组合(长度为1的组合除外),所以收集这一步也是要在判断startIndex == len(nums)之前
self.result.append(self.path[:]) #浅拷贝
if startIndex == len(nums):
return
#step3
used = set()
for i in range(startIndex, len(nums)):
#去重:若当前元素值小于前一个时(非递增)或者曾用过,跳入下一循环
if (self.path and nums[i]<self.path[-1]) or nums[i] in used:
continue
used.add(nums[i])
self.path.append(nums[i])
self.backtracking(nums, i+1)
self.path.pop()
return self.result
- 上面解法也可ac,但是运行时间要长,主要问题是在最后添加到结果集前才判断组合是否为生序,这与题解给出的在#step3直接判断相比要多做很多工作
class Solution:
def __init__(self):
self.path = []
self.result = []
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
return self.backtracking(nums, 0)
def backtracking(self, nums, startIndex):#step1
#step2
if nums is None:
return []
if len(self.path) > 1: #and self.path == sorted(self.path):
#树形图显示也是要收集所有节点的组合(长度为1的组合除外),所以收集这一步也是要在判断startIndex == len(nums)之前
self.result.append(self.path[:]) #浅拷贝
if startIndex == len(nums):
return
#step3
used = set()
for i in range(startIndex, len(nums)):
#去重:若当前元素值小于前一个时(非递增)或者曾用过,跳入下一循环
if (self.path and nums[i] < self.path[-1]) or nums[i] in used:
continue
used.add(nums[i])
self.path.append(nums[i])
self.backtracking(nums, i+1)
self.path.pop()
return self.result
46.全排列
-
根据example1来画树形图,可以看出每个元素选取之后,之前的做法是self.backtracking(nums, i+1),但是这里是依然可以选取其他的元素。比如,在选取2之后,依然可以选取1和3。然后收集叶子节点的组合到结果集。因此不能再用startIndex了。
-
去重:
借助used数组:存储boolean值来标记一个元素是否已经被选择,因为单个组合内不能有重复的元素。
class Solution:
def __init__(self):
self.path = []
self.result = []
def permute(self, nums: List[int]) -> List[List[int]]:
used = [False]*len(nums)
return self.backtracking(nums, used)
def backtracking(self, nums, used):#step1
#step2
if len(self.path) == len(nums):
self.result.append(self.path[:])#浅拷贝
return
#step3
for i in range(0, len(nums)):
if used[i] == True: # 去重
continue
used[i] = True
self.path.append(nums[i])
self.backtracking(nums, used)
self.path.pop()
used[i] = False
return self.result
直接利用self.path来干used数组的活
class Solution(object):
def __init__(self):
self.path = []
self.result = []
def permute(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
return self.backtracking(nums)
def backtracking(self, nums):#step1
#step2
if len(self.path) == len(nums):
self.result.append(self.path[:])
return
#step3
for i in range(0, len(nums)):
if nums[i] in self.path: #去重
continue
self.path.append(nums[i])
self.backtracking(nums)
self.path.pop()
return self.result
47.全排列 II(medium)
难点在于去重:
为了判断相邻节点是否重复使用,需要对nums进行排序
在判断相邻节点是否相同的同时也要check used[i-1] == 0 (树层去重)
参考回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibili
class Solution:
def __init__(self):
self.path = []
self.result = []
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
nums.sort()
used = [0]*len(nums)
return self.backtracking(nums, used)
def backtracking(self, nums, used):#step1
#step2
if len(self.path) == len(nums):
self.result.append(self.path[:])
return
#step3
for i in range(0, len(nums)):
if (i > 0 and nums[i] == nums[i-1]) and used[i-1] == 0: #去重
continue
if used[i] == 0: #因为全排列问题每次递归都要从0开始索引,这就需要确认该索引元素没被用过才可以
used[i] = 1
self.path.append(nums[i])
self.backtracking(nums, used)
self.path.pop()
used[i] = 0
return self.result
总结:
-
一般来说:组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果。(可通过画树形图看出来)
-
去重首先要画出树形图,然后分析其逻辑