前几天刷题遇到了这样一道题:
给定两个数n和k,要求编码得到1~n中所有可能的k个数的组合
如果k比较小的话(例如<=3),我们是可以用几层循环来穷举所有的可能性的,例如(若k = 3):
1 vector<vector<int>> res; 2 for(int i = 1; i <= n; i++) { 3 for(int j = 1; j <= n; j++) { 4 for(int m = 1; m <= n; m ++) { 5 if(i == j || i == m || j == m)continue; 6 res.push_back({i, j, m}); 7 } 8 } 9 }
但是当k未知的时候,他可能是比较大的,这时候如果再想用循环套循环的方法做,可能就无以为继了。
这时候就需要利用回溯算法的思想来解题了:
首先需要定义两个全局变量,一个用于存储所有可能的组合,一个用于存储单一的符合条件的组合:
1 vector<vector<int>> res; //存放所有结果 2 vector<int> tmp; //存放单个结果
接着是回溯函数的主体,其中第三个变量是在n个数中开始遍历的位置:
1 void backtracking(int n, int k, int start) {}
在回溯的过程中,如果tmp的长度等于k的话,就说明这个结果是符合题意的一种组合,就可以将该结果储存到res中去了,并结束该轮的递归:
1 if(tmp.size() == k) { 2 res.push_back(tmp); 3 return; 4 }
然后就是回溯函数中的循环了,首先是将每次循环开始时候的那个数存入到tmp中去,再开始递归回溯,然后再将这个数从tmp中删去,再开始新一轮的循环:
1 for(int i = start; i <= n; i++){ 2 tmp.push_back(i); 3 backtracking(n, k, i + 1); //开始递归插入下一个元素 4 tmp.pop_back(); //删除插入的元素并开始新一轮循环 5 }
完整的回溯函数如下所示:
1 void backtracking(int n, int k, int start) { 2 if(tmp.size() == k){ 3 res.push_back(tmp); 4 return; 5 } 6 for(int i = start; i <= n; i++) { 7 tmp.push_back(i); 8 backtracking(n, k, i + 1); //开始递归插入下一个元素 9 tmp.pop_back(); //删除插入的元素并开始新一轮循环 10 } 11 }
以n = 4, k = 2为例:
将start设为1,开始循环的时候,1会被插入到tmp中去,并以2为start开始递归,再将2插入到tmp中去,再以3为start开始递归,此时tmp的长度等于k, 为2,因此将{1,2}添加到res中去,并直接return,此时会回到上一步骤中的删除元素中去,将tmp中的2删除,接着开始循环,将3插入tmp中去,以此往复......
最后只要在主函数中调用回溯函数,就可以完成这道题目了:
vector<vector<int>> res; vector<int> path; vector<vector<int>> combine(int n, int k) { backtracking(n, k, 1); return res; } void backtracking(int n, int k, int start){ if(tmp.size() == k){ res.push_back(tmp); return; } for(int i = start; i <= n; i++){ tmp.push_back(i); backtracking(n, k, i + 1); tmp.pop_back(); } }
这道题目只是回溯算法中的一道基础题,不过掌握了这种思想的话,再多的难题都能够迎刃而解~
posted on
浙公网安备 33010602011771号