全排列的概念
给定一个整数n,我们将1-n这n个数字进行任意序列的排列,如果使用穷举法,显然时间复杂度是O(n!),很不经济,在《组合数学》(Richard A.Brualdi)数中介绍了一种可移动元素法生成n个数的全排列的方法。这篇文章使用Java语言实现这种算法。
算法思想
首先使用数组存放初始序列,并为序列中的每个元素规定一个方向(左或者右),初始状态所有元素方向相同(本算法中初始化为左方向),我们定义可移动元素:如果一个元素k的箭头指向一个与其相邻但比它要小的元素,我们称这个k为可移动元素。算法步骤如下:
- 1.初始化数组与其方向。
- 2.当存在可移动元素时,执行如下循环:
- 2.1找出最大的可移动元素m。
- 2.2交换m和与其箭头所指的方向的相邻的元素。
- 2.3改变当前数组中所有大于m的元素的箭头方向。
- 2.4输出当前数组序列,为一个排列。
- 3.退出循环,我们就得到了全排列。
代码
import java.util.Scanner;
public class N_Combination {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
Combination(n);
}
//生成n排列
public static void Combination(int n) {
int[] a=new int[n];//数组a存放1-n数字用来进行排列
int[] b=new int[n];//数组b元素存放方向,约定0代表左方向,1代表右方向
for(int i=0;i<n;i++) {
a[i]=i+1;//初始化数组
b[i]=0;//方向初始化全部向左
}
printArray(a);//先输出初始数组
int max=Active(a, b);//找到当前序列中最大的活动元素
int maxValue=max==-1?-1:a[max];
while(max!=-1) {
if(b[max]==0) {//活动元素向左,与左边元素交换
swap(a,max,0);
swap(b,max,0);
}else if(b[max]==1) {//活动元素向右,与右边元素交换
swap(a,max,1);
swap(b,max,1);
}
for(int i=0;i<a.length;i++) {
if(a[i]>maxValue) {
//b[i]=(b[i]==1?0:1);//把所有值比maxValue大的数调换方向
if(b[i]==0) {
b[i]=1;
}else {
b[i]=0;
}
}
}
printArray(a);//输出当前排列
max=Active(a, b);
maxValue=max==-1?-1:a[max];
}
}
public static int Active(int[] a,int[] b) {//返回最大活动元素下标,如果没有活动元素,返回-1
int maxAc=-1;//存放当前最大活动元素
int maxPointer=-1;//存放当前最大活动元素下标
for(int i=0;i<a.length;i++) {
if(b[i]==0&&i>=1) {
if(a[i]>a[i-1]&&a[i]>maxAc) {
maxAc=a[i];
maxPointer=i;
}
}
if(b[i]==1&&i<=a.length-2) {
if(a[i]>a[i+1]&&a[i]>maxAc) {
maxAc=a[i];
maxPointer=i;
}
}
}
return maxPointer;
}
public static void printArray(int a[]) {//将数组输出
StringBuffer sb=new StringBuffer();
for(int i=0;i<a.length;i++) {
sb.append(a[i]);
}
System.out.println(sb);
}
public static void swap(int a[],int pointer,int direction) {
if(direction==0) {//和左边元素交换
int temp=a[pointer];
a[pointer]=a[pointer-1];
a[pointer-1]=temp;
}
if(direction==1) {//和右边元素交换
int temp=a[pointer];
a[pointer]=a[pointer+1];
a[pointer+1]=temp;
}
}
}