C语言,排列组合算法
2021-07-27 本文已影响0人
taobao
一、全排列
不排序一般做法 递归法:
#include <stdio.h>
#include <stdlib.h>
//递归
void traverse(int *a, int index, int num);
//交换
void swap(int *a, int *b);
int main(int argc, char *argv[])
{
//获取输入数字
int num = 0;
scanf("%d", &num);
printf("%d\n", num);
//初始化
int a[num];
for(int i=0; i<num; i++) {
a[i] = i+1;
}
//递归调用
traverse(a, 0, num);
}
void traverse(int *a, int index, int num)
{
if (index == num) {
//最后遍历输出
for(int i=0; i<num; i++) {
printf("%d", a[i]);
}
printf("\n");
} else {
for(int i=index; i<num; i++) {
//当前位的数,和后续几位,逐一替换
swap(&a[i], &a[index]);
traverse(a, index+1, num);
//再次替换,相当于撤销替换
swap(&a[i], &a[index]);
}
}
}
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
输入:3
输出:
123
132
213
231
321
312
参考图片:
image.png
排序做法:(假设从小到大排)
针对排序的核心思想:在第一交换后,递归前,将从index往后的数据,进行从小到大排序,
如上图: 当123交换成 321的时候,将321转换成312,保持除第一位3之后的另外两位的有序,
#include <stdio.h>
#include <stdlib.h>
//递归
void traverse(int *a, int index, int num);
//交换
void swap(int *a, int *b);
int main(int argc, char *argv[])
{
//获取输入数字
int num = 0;
scanf("%d", &num);
printf("%d\n", num);
//初始化
int a[num];
for(int i=0; i<num; i++) {
a[i] = i+1;
}
//递归调用
traverse(a, 0, num);
}
void traverse(int *a, int index, int num)
{
if (index == num) {
//最后遍历输出
for(int i=0; i<num; i++) {
printf("%d", a[i]);
}
printf("\n");
} else {
for(int i=index; i<num; i++) {
//当前
swap(&a[i], &a[index]);
//从index后,数据要排序 ,这对从小到大排序 针对递增
int temp[num]; //为了方便递归后的撤销,这里申请变量
for(int i=0; i<num; i++) {
temp[i] = arr[i];
}
for(int m=index+1; m<num-1; m++) {
for(int n=m+1; n<num; n++) {
if (temp[m] > temp[n]) {
swap(&temp[m], &temp[n]);
}
}
}
reverse(temp, index+1, num);
//撤销替换
swap(&a[i], &a[index]);
}
}
}
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
输入:3
输出:
123
132
213
231
312
321
二、包含重复数据的排列组合
在普通排列组合的基础之上,从第一个数字起,每个数与它后面非重复出现的数进行交换。
用编程的话描述就是第i个数与第j个数交换时,要求[i,j)中没有与第j个数相等的数。
#include <stdio.h>
#include <stdlib.h>
void reverse(int *arr, int index, int num);
void swap(int *a, int *b);
int main(int argc, char *argv[])
{
//处理输入参数
int num = 0;
scanf("%d", &num);
int arr[2*num];
for(int i=0; i<num; i++) {
arr[2*i] = i+1;
arr[2*i+1] = i+1;
}
reverse(arr, 0, 2*num);
}
void reverse(int *arr, int index, int num)
{
if (index == num) {
for(int i=0; i<num; i++) {
printf("%d ", arr[i]);
}
printf("\n");
} else {
for(int i=index; i<num; i++) {
// 是否可以交换 0否 1是
int isChange = 1;
//如果是开始
if (i!=index) {
//i=index,是可以进行交换的
//判断[index,i)之间是否有跟arr[i]重复的数,有则不进行交换
for(int m=index; m<i; m++) {
if (arr[m] == arr[i]) {
isChange = 0;
m = i;
}
}
}
if (isChange) {
swap(&arr[i], &arr[index]);
reverse(arr, index+1, num);
swap(&arr[i], &arr[index]);
}
}
}
}
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
二、组合
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* arr 待组合数据
* res 组合结果保存数据
* index 当前要写入结果的key
* start 当前递归开始位置
* max arr最大长度
* num 当前尚需组合的个数
* len 组合后结果的长度,方便输出
*/
void recursion(int *arr, int *res, int index, int start, int max, int num, int len);
int main(int argc, char *argv[])
{
int max = 0, num = 0;
scanf("%d %d", &max, &num);
int arr[max];
memset(arr, 0, sizeof(arr));
for(int i=0; i<max; i++) {
arr[i] = i+1;
}
//组合数据
int res[num];
memset(res, 0, sizeof(res));
recursion(arr, res, 0, 0, max, num, num);
return 0;
}
void recursion(int *arr, int *res, int index, int start, int max, int num, int len)
{
if (num == 0) {
//当待填充的个数为0时,表示结束
for(int i=0; i<len; i++) {
printf("%d ", res[i]);
}
printf("\n");
} else {
for(int i=start; i<=max-num; i++) {
//针对当前index, 遍历剩余可用的数填充递归
res[index] = arr[i];
recursion(arr, res, index+1, i+1, max, num-1, len);
}
}
}