关于全排列算法
首先介绍:邻位互换算法
原理:
首先会先给数据排序(从小到大),然后给这些数据赋上移动数据,比如最开始都给他们赋值为-1表示能向左移,其实在这里只用一个新数组来存储移位数据就可以了,比如我们现在3是可以左移的,3的位置是2,然后通过去找移位数组里面的值,发现对于位置2的值为-1,只用拿2-1就可以得到,3移位后的位置了。
其次如果3移动到最左边就不能再移位了,这个时候要去找剩余元素里面最大的元素,通过移动它一次,然后再改变3的移动方向,继续移位。(注意移动方向的元素,必须要小于移动元素)最后会发现都不能再移动了。
上代码:
# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>
using namespace std;
const int N_MAX = 10;
int n;
int a[N_MAX + 10];
int face[N_MAX + 10];
bool canMove(int x)
{
int y = x + face[x];
return y >= 1 && y <= n && a[x] > a[y];//判断移位是否已经到达边界,并且移动的下一个元素要小于此时移动的元素
}
bool permutation()
{
int a_max = 0, pos = -1;
for (int i = 1; i <= n; i++) {
if (!canMove(i) || a_max > a[i]) continue;
a_max = a[i], pos = i;
}
if (pos == -1) return false;
int pos_to = pos + face[pos];
swap(a[pos], a[pos_to]);
swap(face[pos], face[pos_to]);//!!!很重要,元素头上那个箭头要跟着元素移动哦
for (int i = 1; i <= n; i++)
if (a[i] > a_max) face[i] = -face[i];
return true;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
a[i] = i;
memset(face, -1, sizeof(face));
do {
for (int i = 1; i <= n; i++)
printf("%5d", a[i]);
printf("\n");
} while (permutation());
return 0;
}
递归实现算法:
原理:比如现在我们有一个数组{1,2,3,4}要求它的全排列,其实可以想象为先求首元素是1的2,3,4的全排列,然后首元素为2的1,3,4的全排列。。。。。。
2,3,4的全排列又可以想象成去求首元素是2的3,4的排列,依次减治下去!!!
上代码:
#include <stdio.h>
int n = 0; //n用来计全排列的总数
int main() //主函数
{ int list[] = {3,5,8,2};
perm(list, 0, 3);
printf("total:%d\n", n); //输出n的个数
}
void perm(int list[], int begin, int end) //产生list[begin:end]的所有排列
{
int i;
if (begin == end) //begin等于end,即list中只有一个元素时
{
for(i = 0; i <= begin; i++)
printf("%d ", list[i]); //打印出这个排列
printf("\n");
n++; //n的个数加一,表示又有一种情况产生了
}
else
{
for(i = begin; i <= end; i++)
{
swap(&list[begin], &list[i]);
perm(list, begin + 1, end);
swap(&list[begin], &list[i]); //必须要在递归下去的时候将已经移位的数据进行还原,比如1,2,3,4你进行到了2,3交换的时候,然后进入递归此时数组为1,3,2,4就只用再交换2,4就行了,然后退回到递归之前,这个时候你要还原数组为1,2,3,4这时2,就会和4交换。因此所有情况都会遍历
}
}
}
void swap(int *a, int *b) //用来交换两个变量值的函数
{
int m;
m = *a;
*a = *b;
*b = m;
}

浙公网安备 33010602011771号