题目:
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
题解:
递归回溯
递归回溯的一一列举每种情况即可。
注意:
1. 递归的时候,可以选择print一些参数出来,方便调试。
2. 注意遍历的深度,以及在每一个位置,应该遍历到哪个数字为止
Code:
class Solution:
# @return a list of lists of integers
def __init__(self):
self.a = []
self.ans = []
def make(self, x, p, n, k):
print (x,p,n,k)
if p == k:
self.ans.append(list(self.a))
print(self.a)
else:
for i in range(x+1, n-k+p+2):
self.a.append(i)
self.make(self, i, p+1, n, k)
self.a.pop()
def combine(self, n, k):
if n == k:
self.ans = [list(range(1,n+1))]
else:
self.a = []
self.ans = []
for i in range(1,n-k+2):
self.a.append(i)
self.make(self, i, 1, n, k)
self.a.pop()
return self.ans
但是,这个代码太丑了!...并且,会超时...
递归需要系统堆栈,消耗的空间大,而且消耗的时间也多。
大部分的递归都能改成循环或者递推!
题解2:
生成第一个list,即 [1,2,3...k]
然后直接在这个list上面操作:
每次都是在最后一位(即第k位)上+1,若第k位超过了n,那么从k-1位由后往前依次检查每位,是否比n-k+i大(i是第几位)(即当前这位是否已经到达了这位可以取的数字的上限)
例如,n=6, k = 4
现在是[2,3,5,6],最后一位+1,超过了n=6,那么从k-1位开始向前检查,k-1位是5,已经取到了这位的上限(不能取6),然后检查k-2位,3,还可以+1.
那么,找到还未到达上限的位置之后,将这个位置上的数字+1,后面的数字依次+1
即,下一个是[2,4,5,6]
当检查,检查到第一位,发现也已经到达上限,那么break,结束。
Code2:
class Solution:
def combine(self, n, k):
a = list(range(1,k+1))
ans = []
ans.append(list(a))
while sum(a) < n*k:
a[k-1] += 1
i = k-1
if a[i] > n:
i -= 1
while i>=0 and a[i]>n-k+i:
i -= 1
if i == -1:
break
a[i] += 1
i += 1
while i < k:
a[i] = a[i-1]+1
i+=1
ans.append(list(a))
return ans
注意:
每次我们都是将产生的list加入answer list,然后再在这个list上修改,那么当我们将当前list加入answer list的时候,我们需要使用深拷贝而不是浅拷贝。
如果我们直接使用ans.append(a)的话,即为浅拷贝,当我们修改a中元素的时候,ans中的元素也会对应被修改。所以,我们要使用ans.append(list)
Python的浅拷贝和深拷贝:
1.浅拷贝:使用copy.copy,它复制了对象,但对于对象中的元素,依然使用引用。
2.深拷贝:使用copy.deepcopy,不仅复制了对象,同时也复制了对象中的元素。
3.复制列表L:list(L)
4.复制字典d:dict(d)
5.复制集合s: set(s)