//按字典次序生成集合的排列
//一个有n个不重复元素{123...n}的集合S,有n!个不同排列。
//他的最小排列是123...n,最大排列是n(n-1)...1
//S的字典次序排列是指从其最小排列123...n开始到最大排列n(n-1)...1为止,
//每一个当前排列比下一个排列小。
//生成集合S的字典次序的排列算法如下:
//1)将最小排列123...n作为当前排列
//2)从右向左找到第一个变换位置
//2.1)第一个变换位置的特点是它小于与其相邻的右边的元素,并且它右边的排列是它右边所有元素的最大排列。
//2.2)找到变换位置后,再在其右边所有比变换位元素大的元素中找到一个最小的元素,将这个最小元素与变换位元素交换。
//2.3)再将变换位右边的所有元素按升序排列,得到的这个新排列即是前面的当前排列的下一排列。
//3)如此反复,直到最后一个排列。
//生成P(n,r)的排列,可以先生成C(n,r)子集排列,然后对每个r子集进行P(r,r)排列。
public class Permutation
{
public static void main(String[] args)
{
//指定集合的元素个数N
int N=Integer.parseInt(args[0]);
//使用一个长度为N的一维数组来表示这个排列
int[] p=new int[N];
//初值为123...n
for(int i=0;i<p.length;i++)
p[i]=i+1;
show(p);
//
int i,j;
int l,r;
//当前排列不是最后一个排列时就继续生成下一个排列
while(true)
{
//从右向左找第一个变换位i,特点是p[i]<p[i+1]
for(i=N-2;i>=0 && p[i]>p[i+1];i--);
//如果找不到变换位,说明当前排列已是最大排列,结束生成排列。
if (i<0) break;
//找到变换位后,在变换位的右边找比他大的最小元素这p[j](有p[j]=p[i]+1)
for(j=N-1;j>i && p[i]>p[j];j--);
//找到比变换元素p[i]大的最小元素p[j]后交换两元素
exch (p,i,j);
//变换位右边的元素按升序排列
//在i,j元素交换前变换位右边的元素已是降序排列,i,j位置的元素交换后这个性质仍成立,所以右边的降序排列变更为升序排列时两端向中间交换即可得到
l=i+1;
r=N-1;
while(l<r)
exch(p,l++,r--);
//显示排列
show(p);
}
}
private static void exch(int[] a,int i,int j)
{
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
//显示这个数组中的值
private static void show(int[] a)
{
for(int i=0;i<a.length;i++)
StdOut.printf("%d",a[i]);
StdOut.println();
}
}