思路:为了使用非递归的方法生成全排列,我们想将所有排列与另外一个有序序列产生一一对应,这样在循环下就可以完成它们的枚举。于是考虑到用字典序来反应有序性。其实也就是将每个排列对应与一个N进制数罢了(N为数组的元素个数)。那么第一步就是将所给数组按升序排列,然后逐个输出字典序大于目前数组字典序的最小的数组排列。
那么如何逐个输出字典序大于目前数组字典序的最小的数组排列呢?
我们肯定是尽量少重排前面的位而多重排后面的位实现数值(字典序)变大得尽量小,(比如1,2,3,4,5应该先是1,2,3,5,4然后是1,2,4,3,5;1,2,4,5,3...)即我们应该是从最小字典序序列开始先重排最后两位,然后是最后三位,。。。,而每次重排完最后x位的标识是最后x位是降序的。应当注意的是,在排最后三位时亦涉及排最后两位的问题,所以,我们算法的第一步就是从后往前找到第一个数a[i]使得a[i] < a[i+1]。那么a[i+1],a[i+2],...,a[n]就是一个降序的序列。这意味着重排最后(n-i)位完成了,我们要开始重排最后(n-i+1)位了。接下来要做的就是在这(n-i+1)个数中找到大于a[i]的最小的数,根据这个数组的性质,我们算法的第二步就是从后往前找到第一个a[k](由于降序,所以它同时也是满足条件的最小的数)使得a[k] > a[i],然后交换他们的位置。交换完位置并不影响a[i+1],a[i+2],...,a[n]的降序性。我们算法的第三步就是将a[i+1],a[i+2],...,a[n]倒置。这样就使得后面的这些数字典序最小,也就使得整个数组的字典序为大于原字典序的最小值。
C语言实现如下:
#include<stdio.h>
#define N 4
int arr[N];
int buff[N];
int main()
{
//init sortedly
for(int i = 0; i < N; i++){
arr[i] = i + 1;
}
//print the permutation
int i = N - 2;
while (i >= 0){
if (arr[i] < arr[i + 1]){
int k = N - 1;
while (arr[k] <= arr[i]){
k--;
}
arr[i] ^= arr[k];
arr[k] ^= arr[i];
arr[i] ^= arr[k];
for(int j = i + 1; j < N; j++)
buff[j] = arr[j];
for(int j = i + 1; j < N; j++)
arr[j] = buff[N - j + i];
for(int n = 0; n < N - 1; n++)
printf("%d ", arr[n]);
printf("%d\n", arr[N - 1]);
i = N - 2;
}
else
i--;
}
return 0;
}