首先說排列,可以根據遞歸定義,從n個元素裏面選m個進行排列,第一步先從n個元素裏面選任意一個元素,然後再在剩下的n-1個元素裏面選m-1個元素進行排列。這個就是遞歸定義。因爲是任意一個元素,因此必須進行一個從第0個元素到n-1個元素的循環。
比如從集合[1,2,3]中取2個數進行排列
(1)第一個循環,取出元素1作爲集合的第一個排列數;然後再利用遞歸從剩下的元素[2,3]中取1個元素進行排列,這個得到的集合就是[2]和[3];最後再將第一個排列數1加到第二步遞歸得到的所有集合中,即得到第一個循環找出的兩個集合[1,2]和[1,3]
(2)第二個循環,取出元素2作爲集合的第一個排列數;然後再利用遞歸從剩下的元素[1,3]中取1個元素進行排列,這個得到的集合就是[1]和[3];最後再將第一個排列數2加到第二步遞歸得到的所有集合中,即得到第二個循環找出的兩個集合[2,1]和[2,3]
(3)依次類推,當所有的循環完成後,把每一趟循環得到的結果集都收集起來,就是這個3取2排列的所有結果。
然後說組合。從n個元素裏面取m個進行組合,安照數據公式的理解,組合比排列要複雜一些,其實排列數已經包含了組合數,只是組合數不需要包含重複的集合,比如集合[1,2,3]和集合[3,2,1]就是不同的排列,但是是同一種組合。在計算組合數的時候,需要注意不要有重複的組合。組合也是利用遞歸來求解。不同的組合,要點就是其中某個集合包含某個元素,其他元素不包含這個元素,就構成不同的集合。也是從n個元素從取任意一個元素,表示爲Ni,表示m個元素的i組合中,必定含有元素Ni。然後再對剩下的數求m-1個元素的組合,此時要注意,後續的遞歸中,原始數據集中必不包含Ni這個元素。這也需要一個循環。比如從集合[1,2,3]從取2個元素的組合:
(1)第一趟循環,取元素1爲第一個組合數,然後對集合[2,3]求取1個元素的組合,這兩個結果爲[2]和[3];然後再加入第一個組合數1,則得到組合[2,1]和[3,1]
(2)第二趟循環,取元素2爲第一個組合數,然後對集合[3]求取1個元素的組合(此時初始數據已經不能包含1了,因爲在第一趟循環的時候,含有元素1的所有組合已經存在了),結果只有[3];然後加入第一個組合數2,則得到組合[3,2]
(3)如果此時剩下的元素不足m個,則不需要進行無謂的循環了,此時已經找出所有的組合
代碼如下:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
//從集合中取m個元素的所有組合
vector<vector<int>> Combine(vector<int> &vt, int m)
{
vector<vector<int> > ret;
if (vt.size() < m)
{
return ret;
}
//如果是集合大小vt.size() 和 m相同,也可以確定結果,相當於在有m個元素的集合裏面取m個元素的組合,只有一種
//因此不需要往後做多餘的遞歸調用
if (vt.size() == m)
{
ret.push_back(vt);
return ret;
}
//遞歸出口
if (1 == m)
{
for (auto &item : vt)
{
vector<int> vtTemp;
vtTemp.push_back(item);
ret.push_back(vtTemp);
}
return ret;
}
int i = 1;
for (auto it = vt.begin(); it != vt.end() && (vt.size() - i) >= (m - 1); it++, i++)
{
//單趟循環,得到的集合中,取出的m個元素中必含有當前元素it,下一個循環得到的集合中,必不含有前一個循環必定會包含的元素
//這就做到每次循環都不會有重複的集合
//依次對剩下的元素進行取m-1個元素的遞歸調用
//但是剩下的元素要滿足元素個數大於等於m-1這個條件,避免無謂的遞歸調用
if (it + 1 != vt.end())
{
vector<int> vtBack(it + 1, vt.end());
vector<vector<int>> vtCombi1 = Combine(vtBack, m - 1);
//加入當前元素
for (auto &item : vtCombi1)
{
item.push_back(*it);
}
ret.insert(ret.end(), vtCombi1.begin(), vtCombi1.end());
}
}
return ret;
}
//從集合中取m個元素進行全排,遞歸實現
vector<vector<int> > Arrange(vector<int> &vt, int m)
{
vector<vector<int> > ret;
if (vt.size() < m)
{
return ret;
}
//遞歸出口,當遞歸到m=1時,就可以確定所有的集合
if (1 == m)
{
for (auto &item : vt)
{
vector<int> vtTemp;
vtTemp.push_back(item);
ret.push_back(vtTemp);
}
return ret;
}
for (auto it = vt.begin(); it != vt.end(); it++)
{
//取集合中的一個元素,然後讓剩下的元素取m-1個元素進行全排,遞歸
vector<int> vtTemp;
//取當前元素的前面部分元素
if (it != vt.begin())
{
vtTemp.insert(vtTemp.end(), vt.begin(), it);
}
//取當前元素的後面部分
if (it + 1 != vt.end())
{
vtTemp.insert(vtTemp.end(), it + 1, vt.end());
}
vector<vector<int> > vtArrage = Arrange(vtTemp, m - 1);//遞歸,分解問題
//把當前元素加入集合,並把集合加入結果集
for (auto &vtSet : vtArrage)
{
auto itInsert = ret.insert(ret.end(), vtSet);
itInsert->push_back(*it);//加入當前元素
}
}
return ret;
}
int main(int argc, char** argv)
{
vector<int> vt = { 1, 2, 3 ,4, 5};
vector<vector<int>> ret = Combine(vt, 2);
//vector<vector<int>> ret = Arrange(vt, 2);
for (auto &vtInt : ret)
{
for (auto &item : vtInt)
{
cout << item << "";
}
cout << endl;
}
cout << "size:" << ret.size() << endl;
return 0;
}