第n次全排列
在我前几篇博客里提到了求下一次的全排列,今天我来写第n次全排列的方法。
给定一组数据1 2 3 4 5,求第25次的全排列,通过分析可以发现,这里面排序的下标是有规律的,比如这组数据,一共有120种排列方式,其中第一个元素为1的有24种,2,3,4,5均各有24种,而第一个元素为1第二个元素为2的的6种,3,4,5均各有6种,然后是3 4 5三个数的排列。由此我们很容易可以找到其中的规律:
1.N个数据有N!种情况。
2.第n个全排列的首个元素为下标为n / (N - 1)!的元素。
3.若n为奇数,则全排列的最后两个数的下标为升序,反之则为降序
通过这些规律我们就能通过编码把第n次的全排列求出来,具体实现为:取得第一个元素后,用递归求出第二个元素,直到剩下两个元素,按规律3判断是否应该改变其顺序。代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
#include <string.h>
#include <math.h>
#define MAX 100
int mul(int n)
{//求阶乘
if(n < 2)
return 1;
return n * mul(n - 1);
}
int get_next(int n, int next)
{//获得下一个指定位置
while(n - next > 0)
n -= next;
return n;
}
********************************************
//函数名:fun
//功能:获得指定次数的全排列
//参数:S为用户输入要排列的字符串,LEN为该字符串长度,
// firstPos为指定次数,nextPos为
// 递归后下一次的指定位置,resu保存结果,lenR是resu的长度
*********************************************/
void fun(char* s, int len, int firstPos, int nextPos, char* resu, int lenR)
{
int num = mul(len) + 1;
int seg = num / len;
if(len < 3)
{//递归出口,当传入的字符串长度小于3时
if(firstPos % 2 == 0)
{//若用户输入次数为偶数则交换后两个字符,否则不交换
resu[lenR - 1] = *(s + len - 2);
resu[lenR - 2] = *(s + len - 1);
return;
}
else
{
resu[lenR - 1] = *(s + len - 1);
resu[lenR - 2] = *(s + len - 2);
return;
}
}
char* temp = new char[len - 1];//用于存储剩余字符
for(int j = 0; j < num; j++)
{
int dis = num - seg*j;
if(nextPos >= dis)//从后往前作减法,直到小于用户输入的次数
{
int index = len - j;
*(resu + lenR - len) = *(s + index);//赋值
for(int k = 0, c = 0; k < len; k++)
{//取剩下的字符串
if(k == index)
continue;
*(temp + c++) = *(s + k);
}
break;
}
}
//递归调用
fun(temp, len - 1, firstPos, get_next(nextPos, seg), resu, lenR);
}
int main()
{
//freopen("in.txt","r",stdin);
char s[MAX] = "abcdefghijklmnopqrstuvwxyz";
long long int n = 26;
//cin >> s >> n;
//cin >> n;
int len = strlen(s);
n = n > 0 ? n : 1;//保证字符串在1到最大阶乘数之间
n = n > mul(len) ? mul(len) : n;
for(int i = 10; i < 20 ; i++)
unsigned long long m = mul(i);
char* resu = new char[len];
for(int i = n; i <= n; i++)
{//从1开始一直到n的全排列
fun(s, strlen(s), i, i, resu, len);//得到第n次排列
for(int j = 0; j < len;j ++)
cout << *(resu + j);
cout << endl;
}
system("pause");
return 0;
}