问题描述
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
问题分析
全面做过类似的。只要用回溯法+DFS就能解决。但是纯DFS很慢,我们要控制一下递归深度,对没必要递归下去的趁早结束。这样可以beat 80%的人。
但是我们并不满足于beat 80%的人。我们怎么也要beat 90%吧。
但是,这段代码已经是回溯法的极限了,所以我们要另辟蹊径。
我们也可以用迭代的方式来做这个题目。
我们通过观察,发现结果集有以下特点:
元素都是升序的
元素的最小值是1,最大值是n。
结果集的长度为k。
这是结果集的充要条件。即,我们不用想那么多,我们只需要求出来满足这三个条件的序列即可。
我们能够想到的最简单的结果序列就是1…k.
最简单的最后的结果序列是 n-k…n
然后我们怎么得到下一个序列呢?
根据我们的规则,我们把这个序列变为下一个序列的最小代价就是修改1个元素。修改哪个呢?
1…k,显然修改k为k+1是最符合题意的。问题来了,我们再求下一个序列,怎么做? 是继续修改k+1,还是修改k+1前的值?
这就是我们思考的重点,如果继续修改k+1,是挺简单的,但是会给我们进行下一轮的修改带来麻烦。所以我们用一种投机取巧的方式:
- 我们每次都修改这个序列中第一个不符合条件的元素。
- 如果扫描一遍都符合,那么我们就该退出了(因为这时我们已经得到n-k…n了)
AC代码
class Solution:
def combine(self, n: int, k: int):
nums = [i for i in range(1,n+1)]
self.used = [False for i in range(n)]
self.res = []
self.solution(nums, [], 0, len(nums), k)
print(self.res)
return self.res
def solution(self,nums,curs,left,right,depth):
for i in range(left,right):
if self.used[i] or right - i + len(curs) < depth:
continue
curs.append(nums[i])
self.used[i] = True
if len(curs) < depth:
self.solution(nums,curs,i+1,right,depth)
else:
self.res.append(curs[:])
curs.pop()
self.used[i] = False
s = Solution()
s.combine(5,5)
AC代码2
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
nums = list(range(1, k + 1)) + [n + 1]
output, j = [], 0
while j < k:
output.append(nums[:k])
j = 0
while j < k and nums[j + 1] == nums[j] + 1:
nums[j] = j + 1
j += 1
nums[j] += 1
return output