1 排列
1.1 参考
(1)C语言如何打印一个数组排列组合?https://segmentfault.com/a/1190000000725176
1.2 思路
n个元素选择m个进行排列
数组arr,长度为n,找出从中取出m个数的所有组合。例如,{1,2,3}中选2个全排列如下:{1,2},{1,3},{2,1},{2,3},{3,1},{3,2}。假设数组的元素没有重复,取出的m个数也没有重复。
参考1中提供了思路如下:
1.把第1个数换到最前面来(本来就在最前面),准备打印1xxx,再对剩下的数做全排列。
2.把第2个数换到最前面来,准备打印2xxx,再对剩下的数排列。
……
3.把第n个数换到最前面来,准备打印3xxx,再对剩下的数做全排列。
注意:由此可见按顺序将数组中的1个数交换到最前面的位置,然后递归解决,把对整个序列做全排列的问题归结为对它的子序列做全排列的问题。递归边界是已经排列到m个元素。
1.3 c++实现
#include <iostream>
#include <algorithm>
//#include <cstdio>
using namespace std;
/**打印从n个元素的数组中选取m个的排列*/
void printAllPermutation(int arr[], int offset, int n, int m){
// 排列到m个数时
if(offset == m){
// 打印
for(int i = 0; i < m; ++i){
cout << arr[i] << " ";
}
cout << endl;
}
for(int i = offset; i < n; ++i){
swap(arr[i], arr[offset]);
printAllPermutation(arr, offset+1, n, m);
swap(arr[i], arr[offset]);
}
}
int main()
{
//freopen("out.txt", "w", stdout);
int arr[]= {1,2,3,4,5,6};
int r = 4;
printAllPermutation(arr, 0, sizeof(arr)/sizeof(arr[0]), r);
//fclose(stdout);
return 0;
}
2 组合
2.1 参考
(1)POJ 1753 Flip Game (递归枚举):http://www.cnblogs.com/shuaiwhu/archive/2012/04/27/2474041.html
(2)从数组中取出r个数的所有组合:http://www.acmerblog.com/combinations-of-r-elements-6059.html
(3)从数组中取出n个元素的所有组合(递归实现)http://www.cnblogs.com/shuaiwhu/archive/2012/04/27/2473788.html
2.2 思路
n个元素选择r个进行组合
数组arr,长度为n,找出从中取出r个数的所有组合。例如:对于数组{1, 2, 3, 4} ,r = 2,则打印出:{1, 2}, {1, 3}, {1, 4}, {2, 3}, {2, 4} ,{3, 4}. 也就是 组合公式
Cnm=C24=6
个。 假设数组的元素没有重复,取出的r个数也没有重复。
参考2给出的思路是:
使用递归是比较容易解决,类似分治法。组合数有如下的递归关系:
Crn=Cr−1n−1+Crn−1 (即对第n个物品选择/不选)我们可以考虑对当前的数取还是不取,从而不断缩小问题的范围。
2.3 c++实现
#include <iostream>
using namespace std;
/**打印在含n个元素的数组中选取r个元素所有组合,
公式:C(n, r) = C(n-1, r-1) + C(n, r-1),第n个元素选或不选
arr[] 输入数组,
data[] 保存当前的一个组合
start, end 剩余数组的起始位置
count 已选取元素的个数
r 总共要选取元素的个数
*/
void printAllCombination(int arr[], int data[], int start, int end, int count, int r)
{
// 如果剩下的数组不够(r-count)就直接返回,表示目前选择的方案不可能
if(start + (r - count) > end) return;
// 组合够r个就打印,并返回
if(count == r){
for(int i = 0; i < r; ++i){
cout << data[i] << " ";
}
cout << endl;
return;
}
//挑选当前数字
data[count] = arr[start];
printAllCombination(arr, data, start+1, end, count+1, r);
//不挑选当前数字
printAllCombination(arr, data, start+1, end, count, r);
}
int main()
{
int arr[]= {1,2,3,4,5,6};
int r = 4;
int *data = new int[r];//保存当前的一个组合
printAllCombination(arr, data, 0, sizeof(arr)/sizeof(arr[0]), 0, r);
return 0;
}
附:数组去重
参考:
简单算法:去掉1维数组中的重复项:http://bbs.csdn.net/topics/70049565
看了一下该链接里的解决方案,有以下的几种方法
(1)先对数组进行排序,然后再扫描一遍相邻的已读数组删去
(2)堆排序的同时剔除重复元素,就像STL中的set类做的一样
(3)读数组、插入一棵二叉排序树中、遍历二叉排序树存到数组中。
然而我只看懂了第(1)种,第(2)种没有动手去写,第(3)种不太懂啊,所以直接上第(4)种:
(4)使用std::set, 用原始数组作为set的初始化参数,直接就可以筛去重复元素:
#include <iostream>
#include <set>
using namespace std;
int main()
{
int arr[] = {1, 1, 2, 2, 2, 3, 4, 5, 6, 6};
set<int> arr_set(arr, arr+sizeof(arr)/sizeof(arr[0]));
cout <<"set size: " << arr_set.size() << endl;
for (set<int>::const_iterator itor = arr_set.begin();
itor != arr_set.end(); ++itor){
cout << (*itor) << " ";
}
cout << endl;
}