leetcode回溯算法-排列组合子集专题-刷题记录
组合相关题目:
排列相关题目:
子集相关题目:
1. 回溯枚举的实现(排列与组合)
对于n个元素进行排列或组合,数组长度为k。
组合的代码如下:
def con(self,arr,cur,n,k): #递归停止条件,长度为k或是枚举到第n个元素 if len(arr)==k: self.res.append(arr[:]) return if cur==n+1: return #枚举主体,枚举元素从当前位置开始 for i in range(cur,n+1): arr.append(i) self.con(arr,i,n,k) arr.pop()
排列的代码如下:
def con(self,arr,cur,n,k): #递归停止条件,长度为k或是枚举到第n个元素 if len(arr)==k: self.res.append(arr[:]) return if cur==n+1: return #枚举主体,枚举元素从第一位开始 for i in range(cur,n+1): arr.append(i) self.con(arr,i,n,k) arr.pop()
对比可发现,排列组合的不同之处主要在于枚举主题代码的for循环范围。
由于组合是有序的,(1,2,3)与(3,2,1)是一个结果,那我们就需要枚举对象有序出场,譬如对于本题中n个数需要从小到大出场。则对于当前位置的数字,下一位置的数一定大于当前位的数,那么枚举就要从当前位开始。
组合是无序的,下一位的数与当前位的数没有联系,则下一位的数可以从第0个元素枚举。

2. 元素不可重复与结果不可重复
元素不可重复:假设给定元素[1,7,5,3,1,5]这6个数,给出长度为3的排列与组合,并规定所有元素只能用一次。
结果不可重复:假设给定元素[1,7,5,3,1,5]这6个数,给出长度为3的排列与组合,并规定输出结果不可重复。
元素不可重复的组合:
def con(self,arr,cur,nums,k): #递归停止条件,长度为k或是枚举到第n个元素 if len(arr)==k: self.res.append(arr[:]) return if cur==len(nums): return #枚举主体,枚举元素从当前位置开始 for i in range(cur,n+1): arr.append(i) self.con(arr,i+1,n,k) arr.pop()
与之前不同的是,下一位的元素从cur+1开始。
元素不可重复的排列:
def con(self, arr, cur, nums, k, vis): # 递归停止条件,长度为k或是枚举到第n个元素 if len(arr) == k: self.res.append(arr[:]) return if cur == len(nums): return # 枚举主体,枚举元素从当前位置开始 for i in range(cur, n + 1): if vis[i] == 1: continue arr.append(i) vis[i]=1 self.con(arr, i + 1, n, k) arr.pop() vis[i]=0
在排列中,需要标志每一个元素是否出现过,以vis数组为标志,如果vis[i]==1则代表第i个元素已经使用。(如果给定元素没有重复的,直接判断就行,不需要vis数组做标记)
在结果不可重复这一命题下,我们首先讨论结果不可重复的排列:
结果不可重复比元素不可重复的要求要高,在上述题目中,元素不可重复可以有答案[1,1,3],[1,1,3](第一个元素“1”与第二个元素“1”位置打颠倒)。而在结果不可重复下,则认为两者是一种情况。首先想到的简单解法是每次将结果加入self.res变量时判断一下即可。然后超时了[doge]
自然,判断是偷懒的。一般这种情况需要我们对所有元素进行排序。然后我们规定对于相同的元素“1”,排在前面的必须在前面出场,排在后面的必须在后面出场。这样就排除了相同的元素,互换了位置,导致结果有重复的问题了。(元素不可重复与结果不可重复的差异也就仅在于此)
对此的实现方法是:
if i>0 and nums[i]==nums[i-1] and vis[i-1]==0: return
这句话的翻译是:如果第i个元素与第i-1个元素相同,并且第i-1个元素未被访问使用,则第i个元素不能使用。这句话,拒绝了相同元素排在后面的先被使用,从而只能是排在前面的先被使用。(天才设计这是,正话反说也不太好想得到)
结果不可重复的排列如下:
def con(self,arr,nums,vis): #递归停止条件 if len(arr)==len(nums): self.res.append(arr[:]) return for i in range(len(nums)): #如果当前点已被访问,则跳过 if vis[i]==1: continue if i>0 and nums[i]==nums[i-1] and vis[i-1]==0: continue vis[i]=1 arr.append(nums[i]) self.con(arr,nums,vis) arr.pop() vis[i] = 0
那么,对于结果不可重复的组合实现方式也类似:
def con(self,arr,nums,cur,target): #递归停止条件 if sum(arr) == target: self.res.append(arr[:]) return if sum(arr) > target: return if cur >=len(nums): return #递归主体 for i in range(cur,len(nums)): if i>cur and nums[i]==nums[i-1]: continue arr.append(nums[i]) self.con(arr,nums,i+1,target) arr.pop()
需要注意的是,其判断语句有所不同。
if i>cur and nums[i]==nums[i-1]: continue
该句的翻译是,对于当前位,如果枚举的元素i与i-1相同,且 i != cur,则代表相同的元素 i-1已经在该位枚举过了。因此跳过。

浙公网安备 33010602011771号