一:确定算法
此题只能用暴力法,由于K决定了for循环数量,所以当K很大时暴力法没法写代码。这时就可以考虑用递归K次替代K个for循环,但递归的过程中会出现一个问题:当一个满足条件的组合找到后,如何在原来的基础上递归寻找下一个可能的组合?此时需要将当前组合中的一个数删除,再加入新的数字,才能找到第二个组合,这个删除的过程结合递归,叫回溯!!!(回溯本质上就是“有删除”的递归)
二:回溯问题全部可以转换为“树结构”解决!!!
①宽度确定思路:
我们要从N个数中选择任意K个数的可能组合,必须得暴力选取(一个数一个数选取),所以树的宽度就等于第一次可以取的不同数的个数。这里需要分两种情况讨论:
- 未剪枝:不进行剪枝操作的情况下,本题树的宽度就等于N
- 剪枝:由于需要取K个数,所以当从某个数字开始算起,剩余数字数量小于K时,该数字就没有选取的意义了,此时树宽度为N-K。
②深度确定思路:
重复取数的过程为K次,所以深度为K。
三:代码
①未剪枝
class Solution:
def nfindk(self, n, k, result, temp, startindex) -> List[List[int]]:
if len(temp) == k:
result.append(temp[:])
return
for ind in range(startindex, n+1):
temp.append(ind)
self.nfindk(n, k, result, temp, ind+1)
temp.pop()
return result
def combine(self, n: int, k: int) -> List[List[int]]:
return self.nfindk(n, k, [], [], 1)
②剪枝1
class Solution:
def nfindk(self, n, k, result, temp, startindex) -> List[List[int]]:
if len(temp) == k:
result.append(temp[:])
return
if (n + 1 - startindex) < k - len(temp): return
for ind in range(startindex, n+1):
temp.append(ind)
self.nfindk(n, k, result, temp, ind+1)
temp.pop()
return result
def combine(self, n: int, k: int) -> List[List[int]]:
return self.nfindk(n, k, [], [], 1)
③剪枝2
class Solution:
def nfindk(self, n, k, result, temp, startindex) -> List[List[int]]:
if len(temp) == k:
result.append(temp[:])
return
for ind in range(startindex, n-(k-len(temp)+2)):
temp.append(ind)
self.nfindk(n, k, result, temp, ind+1)
temp.pop()
return result
def combine(self, n: int, k: int) -> List[List[int]]:
return self.nfindk(n, k, [], [], 1)