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:遞歸解法,仿照根據LeetCode:Subsets 的算法1解法,每一個元素有兩種狀態:選擇、不選擇
1 class Solution { 2 public: 3 vector<vector<int> > combine(int n, int k) { 4 vector<vector<int> >res; 5 vector<int>tmpres; 6 helper(1,n,k,tmpres,res); 7 return res; 8 } 9 10 //從[left, right]范圍內選取k個數,tmpres是一個臨時組合 11 void helper(int left, int right, int k, vector<int> &tmpres, vector<vector<int> >&res) 12 { 13 if(right-left+1 < k)return; 14 if(k == 0) 15 { 16 res.push_back(tmpres); 17 return; 18 } 19 //選擇left 20 tmpres.push_back(left); 21 helper(left+1, right, k-1, tmpres, res); 22 tmpres.pop_back(); 23 //不選擇left 24 helper(left+1, right, k, tmpres, res); 25 } 26 };
算法2:還有另一種遞歸的思路,以一個例子來解釋這種方法,假如要在6個元素(1,2,3,4,5,6)中選取3個元素,(我們對選取的元素組合加一個限制:選取的3個元素的相對順序和他們在原始數組中的順序一致,對於組合問題,這個條件對結果沒有影響):
- 選取第一個元素:第一個元素只能從[1,2,3,4]中選取,因為如果第一個元素選擇5或者6,就不能保證選取的3個元素組合滿則上面的限制
- 假設第一個元素選取的是2,那么第二個元素只能從[3,4,5]中選取,原因同上
- 假設第二個元素選取的是4,那么第三個元素只能從[5,6]中選取,原因同上
1 class Solution { 2 public: 3 vector<vector<int> > combine(int n, int k) { 4 vector<vector<int> >res; 5 vector<int>tmpres; 6 helper(1,n,k,tmpres,res); 7 return res; 8 } 9 10 //從[left, right]范圍內選取k個數,tmpres是一個臨時組合 11 void helper(int left, int right, int k, vector<int> &tmpres, vector<vector<int> >&res) 12 { 13 if(k == 0) 14 { 15 res.push_back(tmpres); 16 return; 17 } 18 for(int i = left; i <= right-k+1; i++) 19 { 20 tmpres.push_back(i); 21 helper(i+1, right, k-1, tmpres, res); 22 tmpres.pop_back(); 23 } 24 } 25 };
算法3:非遞歸的實現。回想一下前面求集合的子集問題,這兩個問題都是某個元素而言,選擇或者不選擇。只是組合問題對子集的大小限制為k,因此我們可以在求子集問題的基礎上有如下代碼(具體可以先參考子集問題的博文): 本文地址
class Solution { public: vector<vector<int> > combine(int n, int k) { vector<vector<int> >res; vector<vector<int> >vec(1); for(int i = 1; i <= n; i++) { int len = vec.size(); vector<int> tmp; for(int j = 0; j < len; j++) { tmp = vec[j]; tmp.push_back(i); if(tmp.size() == k)res.push_back(tmp); else vec.push_back(tmp); } } return res; } };
算法4:基於位操作,這里我們主要借助一個二進制操作“求最小的、比x大的整數M,使得M與x的二進制表示中有相同數目的1”,如果這個操作已知,那么我們可以設置一個初始整數bit,bit的低位第1~k個二進制位為1,其余二進制位為0,bit的二進制表示一種組合,然后調用上述操作求得下一個bit,bit的最大值為:bit從低位起第n-k+1~n位等於1,其余位等於0,即(1<<n) - (1<<(n-k),關於這個方法具體可以參考給力!高效!易懂!位運算求組合
1 class Solution { 2 public: 3 vector<vector<int> > combine(int n, int k) { 4 vector<vector<int> >res; 5 vector<int>tmpres; 6 for(int bit = (1<<k) - 1; bit <= (1<<n) - (1<<(n-k)); bit = NextN(bit))//注意bit的最大值為(1<<n) - (1<<(n-k)即選擇n的前k個 7 { 8 tmpres.clear(); 9 for(int i = 0; i < n; i++) 10 { 11 if(bit & (1<<i)) 12 tmpres.push_back(i+1); 13 } 14 res.push_back(tmpres); 15 } 16 return res; 17 } 18 //返回最小的、比N大的整數M,使得M與N的二進制表示中有相同數目的1 19 int NextN(int N) 20 { 21 int x = N&(-N); 22 int t = N+x; 23 return t | ((N^t)/x)>>2; 24 } 25 };
算法5:算法之排列與組合算法這篇文章中還介紹了另一種基於二進制的做法,描述如下:
本程序的思路是開一個數組,其下標表示1到n個數,數組元素的值為1表示其代表的數被選中,為0則沒選中。
首先初始化,將數組前n個元素置1,表示第一個組合為前n個數。
然后從左到右掃描數組元素值的“10”組合,找到第一個“10”組合后將其變為“01”組合,同時將其左邊的所有“1”全部移動到數組的最左端。
當第一個“1”移動到數組的n-m的位置,即n個“1”全部移動到最右端時,就得到了最后一個組合。
例如求5中選3的組合:
1 1 1 0 0
//1,2,3
1 1 0 1 0
//1,2,4
1 0 1 1 0
//1,3,4
0 1 1 1 0
//2,3,4
1 1 0 0 1
//1,2,5
1 0 1 0 1
//1,3,5
0 1 1 0 1
//2,3,5
1 0 0 1 1
//1,4,5
0 1 0 1 1
//2,4,5
0 0 1 1 1
//3,4,5
【版權聲明】轉載請注明出處:http://www.cnblogs.com/TenosDoIt/p/3461555.html