最近看算法设计与分析基础这本书,里面讲到了一个字典序全排列问题,书中的方法是:
(1)从右至左扫描当前的一个排列,需找第一个连续的选择ai和ai+1,使得ai<ai+1。
(2)在尾部存在大于ai的最小数也就是min{aj | aj>ai, j>i},并将它放置在i位置上。
(3)从i+1到n的位置,以元素ai,ai+1,....an.的增序进行填充,其中放在位置i上的元素已经消去。
这个方法也就是字典序非递归法,更简单的描述是:
一般而言,设P是[1,n]的一个全排列。
P=P1P2…Pn=P1P2…Pj-1PjPj+1…Pk-1PkPk+1…Pn
find: j=max{i|Pi<Pi+1}
k=max{i|Pi>Pj}
1, 对换Pj,Pk,
2, 将Pj+1…Pk-1PjPk+1…Pn翻转
P’= P1P2…Pj-1PkPn…Pk+1PjPk-1…Pj+1即P的下一个
确实很巧妙的算法了,算法复杂度为O(n*n!)
我自己也小试牛刀一把,实现了下字典序最基本的递归算法。思路图大致如下
![](http://hi.csdn.net/attachment/201011/5/0_12889331578hh8.gif)
即定义一个isUsed数组,标记1~n数是否使用,在定义一个arr数组,按从小到大顺序存放第 i 位应该存放的数字。当isUsed数组全部标记使用时,输出当前arr数组。
每次从小到大填充arr数组时,从小到大遍历isUsed数组,选出第一个未标记使用的数字,存放到arr数组,并标记isUsed使用,然后arr数组下一位数字的填充(递归)。
算法复杂度为O((n!)^2)
代码如下:
#include <stdio.h>
#define N_MAX 100
void fun(int N, int *isUsed, int k, int *arr)
{
int i=0,j=0;
static int num=0;
for(i=0;i<N;i++)
{
if(isUsed[i]!=0)
continue;
*(arr+k)=i+1;
isUsed[i]=1;
if(k >= (N-1))
{
printf("arr %d: ",++num);
for(j=0;j<N;j++)
printf(" %d",arr[j]);
printf("\n");
}
else
fun(N,isUsed,k+1,arr);
isUsed[i]=0;
}
}
void main()
{
int isUsed[N_MAX]={0};
int arr[N_MAX]={0};
int k=0,n=5;
fun(n,isUsed,k,arr);
}
如参考1中所说,如果要提高性能,可采用并行计算 + nmap将文件映射到内存。
参考:【1】
字典序全排列生成算法提速
【2】
字典序全排列算法研究