写代码的第二十二天
回溯的最后一天!!!加油呀呀呀呀!!!
491.递增子序列
思路
本题中要在原始的数组中进行操作,所以不能像前面的题一样进行排序后操作,一定不要排序,其他的和之前的题做法很类似,但是有两个地方有区别:1、要求最终结果元素至少两个,而上一个题是子集只要是子集就行,所以有空的可能和一个元素的可能,也就是画出来的树所有的结点都要,但本题要求至少两个所以在画出的树中除了上面两层的结点,其他的结点都要;2、要求递增的子序列,所以在不排序的情况下递增,那么就要求每次插入的数据要和path中的最后一个元素比较,如果小于这个元素那么这条路径可以不用向下找了,直接进行下一步即可。
解决问题1:参数和返回值?参数包括传入的nums数组以及用来判断每次遍历从哪里开始的startindex;返回值我们设置了存储的全局变量path和result接收最后结果,所以不需要返回值。
解决问题2:终止条件?本题的终止条件是当startindex大于lens(nums)时停止,但是startindex的遍历范围就是小于数组长度的,所以这个终止条件可写可不写
解决问题3:单次回溯的逻辑?横向for循环,纵向递归。首先要判断当前的path长度是否大于1,如果大于的话才会append进result;然后要设置一个集合uset,用这个集合去判断在这一层中是否使用过当前将要使用的数字,如果使用过那么接下来就不要再使用了会重复;接下来就是纵向的递归,首先要把当前的这个数字add进入集合中,存储好,为了之后的判断,然后将数组append进path,递归调用backtracing,最后将数字pop出来回溯,这里需要强调一下为什么uset的数字不用pop因为每次调用递归都会执行uset = set()这个语句,所以每次递归调用的都是新的uset,所以不用pop。
正确代码:
class Solution:
def __init__(self):
self.path = []
self.result = []
def backtracing(self,nums,startindex):
if len(self.path) > 1:
self.result.append(self.path.copy())
uset = set()
for i in range(startindex,len(nums)):
if len(self.path)!= 0 and nums[i] < self.path[-1] or nums[i] in uset:
continue
uset.add(nums[i])
self.path.append(nums[i])
self.backtracing(nums,i+1)
self.path.pop()
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
self.backtracing(nums,0)
return self.result
46.全排列
思路
因为之前做过子集的题,这个题和子集很像,但是在子集中【1,2】和【2,1】是一样的集合,本题的排列中【1,2】和【2,1】是不一样的,这个是主要区别。
解决问题1:参数和返回值?参数是数组nums和一个记录是否数组中数字是否使用过的判断数组used,返回值设置了全局变量所以不需要设置返回值。
解决问题2:终止条件?画了树之后,本题要的结果是所有的叶子结点,也不需要剪枝什么的,全都要,所以终止条件就是什么时候到叶子结点,也就是path的长度和nums的长度相等的时候到了叶子结点。
解决问题3:单次回溯逻辑?横向for,纵向递归。for循环中每次遍历到nums中的一个数值,然后在used数组中标记这个位置的数值已经被使用过了,然后将这个数值append进path中,进行递归然后将其pop出来,也要将used中刚才改过的值变回原来的值。
错误第一版:解答错误,path中无任何元素。(我还没改出来啊啊啊啊啊让我再想想)
class Solution:
def __init__(self):
self.path = []
self.result = []
def backtracing(self,nums):
if len(self.path) == len(nums):
self.result.append(self.path.copy())
return
for i in range(len(nums)):
self.path.append(nums[i])
newnums = nums[:i] + nums[i+1:]
self.backtracing(newnums)
self.path.pop()
def permute(self, nums: List[int]) -> List[List[int]]:
self.backtracing(nums)
return self.result
正确代码:
class Solution:
def __init__(self):
self.path = []
self.result = []
def backtracing(self,nums,used):
if len(self.path) == len(nums):
self.result.append(self.path.copy())
return #该取的数已经取完了
for i in range(len(nums)):
if used[i] == True:
continue
used[i] = True
self.path.append(nums[i])
self.backtracing(nums,used)
self.path.pop()
used[i] = False
def permute(self, nums: List[int]) -> List[List[int]]:
used = [False] * len(nums)
self.backtracing(nums,used)
return self.result
47.全排列 II
思路
整体思路和上面的题一样,唯一的区别就在于,这个是要去重的,所以一定要先排序,然后判断是否有重复元素,如果有就跳过。
在去重这里有一个地方要注意一下,一般来说我们写的去重是这样的if i > 0 and nums[i] == nums[i-1],但是本题中需要判断在同一层中如果出现相同的那么就要跳过这个数,也就是说当前结点nums[i]的前一个结点nums[i-1]如果已经被使用过了,也就是递归过了所以在used数组中一定是false,所以要加一个判断条件if i > 0 and nums[i] = = nums[i-1] and used[i-1] = = False: continue。
自己做的时候根本想不到加这个条件,啊好烦啊还是没完全弄懂,代码模版倒是记住了。。。。。。
正确代码:
class Solution:
def __init__(self):
self.path = []
self.result = []
def backtracing(self,nums,used):
if len(nums) == len(self.path):
self.result.append(self.path.copy())
return
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i-1] and used[i-1] == False:
continue
if used[i] == True:
continue
used[i] = True
self.path.append(nums[i])
self.backtracing(nums,used)
self.path.pop()
used[i] = False
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
nums = sorted(nums)
used = [False] * len(nums)
self.backtracing(nums,used)
return self.result