用字典序来生成数组的全排列

思路:为了使用非递归的方法生成全排列,我们想将所有排列与另外一个有序序列产生一一对应,这样在循环下就可以完成它们的枚举。于是考虑到用字典序来反应有序性。其实也就是将每个排列对应与一个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;
}

猜你喜欢

转载自blog.csdn.net/chineseyjh/article/details/80937641