文章目录
组合
leetcode链接
1.解法
首先看到这道题首先想到的是暴力解决,即如果是n个数字组成的集合,选k个的话,我们只需要建立k个for循环依次枚举即可。假设k=2,那么会如下所示:
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
cout << i << " " << j << endl;
}
}
但是如果当k很大或者k没有明确的给出的时候,就像这道题,k是作为一个参数给定的,没有具体的数值,我们就不知道该写几个循环了,因此我们要写一个算法,让他自动帮我们写好k个for循环。
在深入思考一下,这道题不就刚好符合我们说的回溯法的定义,即集合的大小为树的宽度,递归(循环)的深度为树的深度。因此我们可以画出下图:
对于这道题,如果画成树的形式就会如上图所示。
-
首先我们先写出该函数头和一些变量来储存结果:
result = [] # 用来存放最终结果 path = [] # 用来存放每条结果 def backtracking(n,k,startindex)
参数的确定是通过分析逻辑来确定的,可以先往下看。
-
接着我们来确定递归结束条件
看上图来讲,就是每当我们取得的数字的个数等于k了,就需要结束该层递归
if len(path) == k: path1 = path.copy() # 这里注意要copy一下,不然回溯的时候会影响result中的值 result.append(path1) return
-
确定逻辑:
第一层for循环要遍历[1,2,3,4],第二层for循环要从上一次循环选中的数+1开始进行遍历。举个例子,如果第一层选中了1,那么第二层循环就要从[2,3,4]中选择;如果第一层选中了2,那么第二层循环就要从[3,4]中选择。这和我们上面写的循环也是对应的。
因此我们可以写出逻辑部分的代码:
for i in range(startindex,n+1): # 定义一个startindex作为本次循环的开始数值 path.append(i) backtracking(n,k,i+1) path.pop()
-
最终代码为:
def combine(n, k): result = [] path = [] def backtracking(n,k,startindex): if len(path) == k: path1 = path.copy() # 这里注意要copy一下,不然回溯的时候会影响result中的值 result.append(path1) return for i in range(startindex,n+1): path.append(i) backtracking(n,k,i+1) path.pop() backtracking(n,k,1) return result
2.总结
组合问题是回溯中非常经典的问题,需要重点掌握。