从这篇文章始,将花连续2篇文章来介绍2个很重要的问题:生成排列和生成子集
它们都有减一治策略的算法,但这2篇文章将不仅仅局限于减一治策略来实现,而将介绍更多的实现算法。这篇介绍排列,下篇介绍子集。
--------------------------------------------------------------------------------------------------------------------------------------------------
介绍3种构造排列的方法:
算法1:减一治从底向上构造排列
假设n-1个元素的排列已经生成好,那么将第n个元素插入到 每一个排列项的 不同位置,就得到一个新的排列项。举个例子:
注意这可以 递归的定义,用二维数组的第一维表示一个排列项,第二维表示排列项的每一位。
public
static
int
[][] getArrange(
int
a) {
// 输出1到a的排列
int [][] result = new int [Fact(a)][a]; // 共Fact(a)个结果,结果的每个数存在第二维中
if (a == 1 )
result[ 0 ][ 0 ] = 1 ;
else // 递归的从下往上构造
{
int [][] temp = getArrange(a - 1 ); // 得到a-1时的结果
int [] aaa = new int [a - 1 ];
int position = 0 ; // result当前可用位置
for ( int i = 0 ; i < temp.length; i ++ ) {
aaa = temp[i]; // 依次取出每个结果
// 对这些结果做插入操作就得到了新的一个排列
for ( int j = 0 ; j < a; j ++ ) // 插入的不同位置决定了一个结果
{
result[position][j] = a;
// 将aaa的位放入剩下的几个位置
for ( int k = 0 , s = 0 ; k < a && s < a - 1 ; k ++ )
if (result[position][k] != a) {
result[position][k] = aaa[s];
s ++ ;
}
position ++ ;
}
}
}
return result;
}
// 输出1到a的排列
int [][] result = new int [Fact(a)][a]; // 共Fact(a)个结果,结果的每个数存在第二维中
if (a == 1 )
result[ 0 ][ 0 ] = 1 ;
else // 递归的从下往上构造
{
int [][] temp = getArrange(a - 1 ); // 得到a-1时的结果
int [] aaa = new int [a - 1 ];
int position = 0 ; // result当前可用位置
for ( int i = 0 ; i < temp.length; i ++ ) {
aaa = temp[i]; // 依次取出每个结果
// 对这些结果做插入操作就得到了新的一个排列
for ( int j = 0 ; j < a; j ++ ) // 插入的不同位置决定了一个结果
{
result[position][j] = a;
// 将aaa的位放入剩下的几个位置
for ( int k = 0 , s = 0 ; k < a && s < a - 1 ; k ++ )
if (result[position][k] != a) {
result[position][k] = aaa[s];
s ++ ;
}
position ++ ;
}
}
}
return result;
}
算法2:Johnson-Trotter算法通过移动来生成排列
给每个元素赋一个方向,这可以用一个数组来表示,0指向左,1指向右。
有了可移动元素的概念,Johnson-Trotter算法的描述如下:
算法证明应该很难,我就照着流程写的:
public
static
int
[][] JohnsonTrotter(
int
a) {
// Johnson-Trotter算法实现排列
int [][] result = new int [Fact(a)][a];
// 得到第一个排列及方向的初始化
int [] direct = new int [a]; // 方向,0指向左,1指向右边
for ( int i = 0 ; i < a; i ++ )
result[ 0 ][i] = i + 1 ;
int position = 1 ; // result下一个可用位置
// int[] aaa = result[0]; // 非常典型的错误!!!aaa此时和result[0]指向同一个引用,最周会导致result[0]是最后一个aaa的值
int [] aaa = new int [a];
for ( int i = 0 ; i < a; i ++ )
aaa[i] = result[ 0 ][i];
while (hasMove(aaa, direct)) {
int maxMove = maxMove(aaa, direct); // 求当前最大移动元素
int indexMax = 0 ; // 最大元素的下标
for ( int i = 0 ; i < aaa.length; i ++ )
if (aaa[i] == maxMove)
indexMax = i;
// 把k和它箭头指向的相邻元素互换
if (direct[indexMax] == 0 ) {
// 元素互换
int temp = aaa[indexMax];
aaa[indexMax] = aaa[indexMax - 1 ];
aaa[indexMax - 1 ] = temp;
// 相应的direct数组也要改变!!!
direct[indexMax] = direct[indexMax - 1 ];
direct[indexMax - 1 ] = 0 ;
} else if (direct[indexMax] == 1 ) {
int temp = aaa[indexMax];
aaa[indexMax] = aaa[indexMax + 1 ];
aaa[indexMax + 1 ] = temp;
// 相应的direct数组也要改变!!!
direct[indexMax] = direct[indexMax + 1 ];
direct[indexMax + 1 ] = 1 ;
}
// 调转所有大于k的元素的方向
for ( int i = 0 ; i < aaa.length; i ++ )
if (aaa[i] > maxMove) {
if (direct[i] == 0 )
direct[i] = 1 ;
else
direct[i] = 0 ;
}
// 将新排列加入到结果列表
for ( int i = 0 ; i < aaa.length; i ++ )
result[position][i] = aaa[i];
position ++ ;
// 错误同上,引用问题!!!这样会导致每个position的值实际上是一直在跟踪aaa
// result[position] = aaa;
// position++;
}
return result;
}
// Johnson-Trotter算法实现排列
int [][] result = new int [Fact(a)][a];
// 得到第一个排列及方向的初始化
int [] direct = new int [a]; // 方向,0指向左,1指向右边
for ( int i = 0 ; i < a; i ++ )
result[ 0 ][i] = i + 1 ;
int position = 1 ; // result下一个可用位置
// int[] aaa = result[0]; // 非常典型的错误!!!aaa此时和result[0]指向同一个引用,最周会导致result[0]是最后一个aaa的值
int [] aaa = new int [a];
for ( int i = 0 ; i < a; i ++ )
aaa[i] = result[ 0 ][i];
while (hasMove(aaa, direct)) {
int maxMove = maxMove(aaa, direct); // 求当前最大移动元素
int indexMax = 0 ; // 最大元素的下标
for ( int i = 0 ; i < aaa.length; i ++ )
if (aaa[i] == maxMove)
indexMax = i;
// 把k和它箭头指向的相邻元素互换
if (direct[indexMax] == 0 ) {
// 元素互换
int temp = aaa[indexMax];
aaa[indexMax] = aaa[indexMax - 1 ];
aaa[indexMax - 1 ] = temp;
// 相应的direct数组也要改变!!!
direct[indexMax] = direct[indexMax - 1 ];
direct[indexMax - 1 ] = 0 ;
} else if (direct[indexMax] == 1 ) {
int temp = aaa[indexMax];
aaa[indexMax] = aaa[indexMax + 1 ];
aaa[indexMax + 1 ] = temp;
// 相应的direct数组也要改变!!!
direct[indexMax] = direct[indexMax + 1 ];
direct[indexMax + 1 ] = 1 ;
}
// 调转所有大于k的元素的方向
for ( int i = 0 ; i < aaa.length; i ++ )
if (aaa[i] > maxMove) {
if (direct[i] == 0 )
direct[i] = 1 ;
else
direct[i] = 0 ;
}
// 将新排列加入到结果列表
for ( int i = 0 ; i < aaa.length; i ++ )
result[position][i] = aaa[i];
position ++ ;
// 错误同上,引用问题!!!这样会导致每个position的值实际上是一直在跟踪aaa
// result[position] = aaa;
// position++;
}
return result;
}
相应的支持方法见后面完整的代码。
算法3:字典序来生成排列,这样生成的排列是按顺序的。
其实其关键在于 怎样按顺序找出一个排列项的下一个排列项(这应该属于高中数学排列组合里面一个还比较难的问题)
设P是1~n的一个全排列:P = P1P2……Pn = P1P2……Pj-1PjPj+1……Pk-1PkPk+1……Pn
(1)从排列的最右端开始, 找出第一个比右边数字小的数字的序号j(j从左端开始计算),即 j=max{i|pi
(2) 在Pj的右边的数字中,找出所有比Pj大的数中最小的数字Pk,即 k=max{i|Pi>Pj}(右边的数从右至左是递增的,因此k是所有大于Pj的数字中序号最大者)
(3)对换Pj,Pk
(4)再将Pj+1……Pk-1PkPk+1Pn反转,即得到排列P的下一个排列。
举个例子:
例如839647521是数字1~9的一个排列。从它生成下一个排列的步骤如下:
自右至左找出排列中第一个比右边数字小的数字4 839647521
在该数字后的数字中找出比4大的数中最小的一个5 839647521
将5与4交换 839657421
将7421倒转 839651247
所以839647521的下一个排列是839651247。
public
static
int
[][] dicArrange(
int
a) {
// 字典序生成排列
int [][] result = new int [Fact(a)][a]; // 结果数组
for ( int i = 0 ; i < a; i ++ )
result[ 0 ][i] = i + 1 ; // 初始化得到第一个排列,升序
int [] aaa = new int [a];
for ( int i = 0 ; i < result[ 0 ].length; i ++ )
aaa[i] = result[ 0 ][i]; // 将aaa作为一个中介,来变化得到不同的排列方式
int position = 1 ;
while ( ! isDesending(aaa)) {
int m = 0 , n = 0 , mindex = 0 , nindex = 0 ;
for ( int i = aaa.length - 2 ; i >= 0 ; i -- )
if (aaa[i] < aaa[i + 1 ]) {
m = aaa[i]; // 自右至左第一个比右边小的数
mindex = i;
break ; // 铸成大错
// System.out.print(m + " ");
}
for ( int i = aaa.length - 1 ; i > mindex; i -- )
if (m < aaa[i]) {
n = aaa[i]; // m的右边比m大的最小的数
nindex = i;
break ; // 铸成大错
// System.out.print(n + " ");
}
// 将m,n交换
aaa[mindex] = n;
aaa[nindex] = m;
// 将mindex以后的位置逆序(从mindex+1开始)
int pairs = (aaa.length - mindex - 1 ) / 2 ; // 交换这么多对即可
for ( int i = 0 ; i < pairs; i ++ ) {
// int s1Index = i + mindex,s1 = aaa[s1Index]; // 错了一个下标
int s1Index = i + mindex + 1 , s1 = aaa[s1Index];
int s2Index = aaa.length - 1 - i, s2 = aaa[s2Index];
aaa[s1Index] = s2;
aaa[s2Index] = s1;
}
for ( int i = 0 ; i < aaa.length; i ++ )
result[position][i] = aaa[i];
position ++ ;
}
return result;
}
// 字典序生成排列
int [][] result = new int [Fact(a)][a]; // 结果数组
for ( int i = 0 ; i < a; i ++ )
result[ 0 ][i] = i + 1 ; // 初始化得到第一个排列,升序
int [] aaa = new int [a];
for ( int i = 0 ; i < result[ 0 ].length; i ++ )
aaa[i] = result[ 0 ][i]; // 将aaa作为一个中介,来变化得到不同的排列方式
int position = 1 ;
while ( ! isDesending(aaa)) {
int m = 0 , n = 0 , mindex = 0 , nindex = 0 ;
for ( int i = aaa.length - 2 ; i >= 0 ; i -- )
if (aaa[i] < aaa[i + 1 ]) {
m = aaa[i]; // 自右至左第一个比右边小的数
mindex = i;
break ; // 铸成大错
// System.out.print(m + " ");
}
for ( int i = aaa.length - 1 ; i > mindex; i -- )
if (m < aaa[i]) {
n = aaa[i]; // m的右边比m大的最小的数
nindex = i;
break ; // 铸成大错
// System.out.print(n + " ");
}
// 将m,n交换
aaa[mindex] = n;
aaa[nindex] = m;
// 将mindex以后的位置逆序(从mindex+1开始)
int pairs = (aaa.length - mindex - 1 ) / 2 ; // 交换这么多对即可
for ( int i = 0 ; i < pairs; i ++ ) {
// int s1Index = i + mindex,s1 = aaa[s1Index]; // 错了一个下标
int s1Index = i + mindex + 1 , s1 = aaa[s1Index];
int s2Index = aaa.length - 1 - i, s2 = aaa[s2Index];
aaa[s1Index] = s2;
aaa[s2Index] = s1;
}
for ( int i = 0 ; i < aaa.length; i ++ )
result[position][i] = aaa[i];
position ++ ;
}
return result;
}
一些支持方法,见三种算法的完整代码:
Arrangement
package
Section5;
import java.util.Scanner;
/* 第5章 减治法 生成排列(3种算法) */
public class Arrangement {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scan = new Scanner(System.in);
int a = scan.nextInt();
// 输出从1到a的排列
int [][] result1 = getArrange(a);
int [][] result2 = JohnsonTrotter(a);
int [][] result3 = dicArrange(a);
System.out.println( " \n减1治自底向上生成排列的算法: " );
for ( int i = 0 ; i < result1.length; i ++ ) {
for ( int j = 0 ; j < a; j ++ )
System.out.print(result1[i][j]);
System.out.print( " " );
}
System.out.println( " \n\nJohnson-Trotter算法通过移动来生成排列: " );
for ( int i = 0 ; i < result2.length; i ++ ) {
for ( int j = 0 ; j < a; j ++ )
System.out.print(result2[i][j]);
System.out.print( " " );
}
System.out.println( " \n\n字典序来构造排列的算法: " );
for ( int i = 0 ; i < result3.length; i ++ ) {
for ( int j = 0 ; j < a; j ++ )
System.out.print(result3[i][j]);
System.out.print( " " );
}
}
public static int [][] dicArrange( int a) {
// 字典序生成排列
int [][] result = new int [Fact(a)][a]; // 结果数组
for ( int i = 0 ; i < a; i ++ )
result[ 0 ][i] = i + 1 ; // 初始化得到第一个排列,升序
int [] aaa = new int [a];
for ( int i = 0 ; i < result[ 0 ].length; i ++ )
aaa[i] = result[ 0 ][i]; // 将aaa作为一个中介,来变化得到不同的排列方式
int position = 1 ;
while ( ! isDesending(aaa)) {
int m = 0 , n = 0 , mindex = 0 , nindex = 0 ;
for ( int i = aaa.length - 2 ; i >= 0 ; i -- )
if (aaa[i] < aaa[i + 1 ]) {
m = aaa[i]; // 自右至左第一个比右边小的数
mindex = i;
break ; // 铸成大错
// System.out.print(m + " ");
}
for ( int i = aaa.length - 1 ; i > mindex; i -- )
if (m < aaa[i]) {
n = aaa[i]; // m的右边比m大的最小的数
nindex = i;
break ; // 铸成大错
// System.out.print(n + " ");
}
// 将m,n交换
aaa[mindex] = n;
aaa[nindex] = m;
// 将mindex以后的位置逆序(从mindex+1开始)
int pairs = (aaa.length - mindex - 1 ) / 2 ; // 交换这么多对即可
for ( int i = 0 ; i < pairs; i ++ ) {
// int s1Index = i + mindex,s1 = aaa[s1Index]; // 错了一个下标
int s1Index = i + mindex + 1 , s1 = aaa[s1Index];
int s2Index = aaa.length - 1 - i, s2 = aaa[s2Index];
aaa[s1Index] = s2;
aaa[s2Index] = s1;
}
for ( int i = 0 ; i < aaa.length; i ++ )
result[position][i] = aaa[i];
position ++ ;
}
return result;
}
private static boolean isDesending( int [] aaa) {
// 判断序列aaa是否为降序
for ( int i = 0 ; i < aaa.length - 1 ; i ++ )
if (aaa[i] < aaa[i + 1 ])
return false ;
return true ;
}
public static int [][] getArrange( int a) {
// 输出1到a的排列
int [][] result = new int [Fact(a)][a]; // 共Fact(a)个结果,结果的每个数存在第二维中
if (a == 1 )
result[ 0 ][ 0 ] = 1 ;
else // 递归的从下往上构造
{
int [][] temp = getArrange(a - 1 ); // 得到a-1时的结果
int [] aaa = new int [a - 1 ];
int position = 0 ; // result当前可用位置
for ( int i = 0 ; i < temp.length; i ++ ) {
aaa = temp[i]; // 依次取出每个结果
// 对这些结果做插入操作就得到了新的一个排列
for ( int j = 0 ; j < a; j ++ ) // 插入的不同位置决定了一个结果
{
result[position][j] = a;
// 将aaa的位放入剩下的几个位置
for ( int k = 0 , s = 0 ; k < a && s < a - 1 ; k ++ )
if (result[position][k] != a) {
result[position][k] = aaa[s];
s ++ ;
}
position ++ ;
}
}
}
return result;
}
public static int [][] JohnsonTrotter( int a) {
// Johnson-Trotter算法实现排列
int [][] result = new int [Fact(a)][a];
// 得到第一个排列及方向的初始化
int [] direct = new int [a]; // 方向,0指向左,1指向右边
for ( int i = 0 ; i < a; i ++ )
result[ 0 ][i] = i + 1 ;
int position = 1 ; // result下一个可用位置
// int[] aaa = result[0]; // 非常典型的错误!!!aaa此时和result[0]指向同一个引用,最周会导致result[0]是最后一个aaa的值
int [] aaa = new int [a];
for ( int i = 0 ; i < a; i ++ )
aaa[i] = result[ 0 ][i];
while (hasMove(aaa, direct)) {
int maxMove = maxMove(aaa, direct); // 求当前最大移动元素
int indexMax = 0 ; // 最大元素的下标
for ( int i = 0 ; i < aaa.length; i ++ )
if (aaa[i] == maxMove)
indexMax = i;
// 把k和它箭头指向的相邻元素互换
if (direct[indexMax] == 0 ) {
// 元素互换
int temp = aaa[indexMax];
aaa[indexMax] = aaa[indexMax - 1 ];
aaa[indexMax - 1 ] = temp;
// 相应的direct数组也要改变!!!
direct[indexMax] = direct[indexMax - 1 ];
direct[indexMax - 1 ] = 0 ;
} else if (direct[indexMax] == 1 ) {
int temp = aaa[indexMax];
aaa[indexMax] = aaa[indexMax + 1 ];
aaa[indexMax + 1 ] = temp;
// 相应的direct数组也要改变!!!
direct[indexMax] = direct[indexMax + 1 ];
direct[indexMax + 1 ] = 1 ;
}
// 调转所有大于k的元素的方向
for ( int i = 0 ; i < aaa.length; i ++ )
if (aaa[i] > maxMove) {
if (direct[i] == 0 )
direct[i] = 1 ;
else
direct[i] = 0 ;
}
// 将新排列加入到结果列表
for ( int i = 0 ; i < aaa.length; i ++ )
result[position][i] = aaa[i];
position ++ ;
// 错误同上,引用问题!!!这样会导致每个position的值实际上是一直在跟踪aaa
// result[position] = aaa;
// position++;
}
return result;
}
private static int maxMove( int [] a, int [] direct) {
// 求最大移动元素
int max = 0 ;
for ( int i = a.length - 1 ; i >= 0 ; i -- ) {
if (direct[i] == 0 && i != 0 )
if (a[i] > a[i - 1 ] && a[i] > max)
max = a[i];
if (direct[i] == 1 && i != a.length - 1 )
if (a[i] > a[i + 1 ] && a[i] > max)
max = a[i];
}
return max;
}
private static boolean hasMove( int [] a, int [] direct) {
// 判断序列a是否存在可移动元素
for ( int i = a.length - 1 ; i >= 0 ; i -- ) {
if (direct[i] == 0 && i != 0 )
if (a[i] > a[i - 1 ])
return true ;
if (direct[i] == 1 && i != a.length - 1 )
if (a[i] > a[i + 1 ])
return true ;
}
return false ;
}
private static int Fact( int a) {
// 求a的阶乘
int fact = 1 ;
while (a != 0 ) {
fact = fact * a;
a -- ;
}
return fact;
}
}
import java.util.Scanner;
/* 第5章 减治法 生成排列(3种算法) */
public class Arrangement {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scan = new Scanner(System.in);
int a = scan.nextInt();
// 输出从1到a的排列
int [][] result1 = getArrange(a);
int [][] result2 = JohnsonTrotter(a);
int [][] result3 = dicArrange(a);
System.out.println( " \n减1治自底向上生成排列的算法: " );
for ( int i = 0 ; i < result1.length; i ++ ) {
for ( int j = 0 ; j < a; j ++ )
System.out.print(result1[i][j]);
System.out.print( " " );
}
System.out.println( " \n\nJohnson-Trotter算法通过移动来生成排列: " );
for ( int i = 0 ; i < result2.length; i ++ ) {
for ( int j = 0 ; j < a; j ++ )
System.out.print(result2[i][j]);
System.out.print( " " );
}
System.out.println( " \n\n字典序来构造排列的算法: " );
for ( int i = 0 ; i < result3.length; i ++ ) {
for ( int j = 0 ; j < a; j ++ )
System.out.print(result3[i][j]);
System.out.print( " " );
}
}
public static int [][] dicArrange( int a) {
// 字典序生成排列
int [][] result = new int [Fact(a)][a]; // 结果数组
for ( int i = 0 ; i < a; i ++ )
result[ 0 ][i] = i + 1 ; // 初始化得到第一个排列,升序
int [] aaa = new int [a];
for ( int i = 0 ; i < result[ 0 ].length; i ++ )
aaa[i] = result[ 0 ][i]; // 将aaa作为一个中介,来变化得到不同的排列方式
int position = 1 ;
while ( ! isDesending(aaa)) {
int m = 0 , n = 0 , mindex = 0 , nindex = 0 ;
for ( int i = aaa.length - 2 ; i >= 0 ; i -- )
if (aaa[i] < aaa[i + 1 ]) {
m = aaa[i]; // 自右至左第一个比右边小的数
mindex = i;
break ; // 铸成大错
// System.out.print(m + " ");
}
for ( int i = aaa.length - 1 ; i > mindex; i -- )
if (m < aaa[i]) {
n = aaa[i]; // m的右边比m大的最小的数
nindex = i;
break ; // 铸成大错
// System.out.print(n + " ");
}
// 将m,n交换
aaa[mindex] = n;
aaa[nindex] = m;
// 将mindex以后的位置逆序(从mindex+1开始)
int pairs = (aaa.length - mindex - 1 ) / 2 ; // 交换这么多对即可
for ( int i = 0 ; i < pairs; i ++ ) {
// int s1Index = i + mindex,s1 = aaa[s1Index]; // 错了一个下标
int s1Index = i + mindex + 1 , s1 = aaa[s1Index];
int s2Index = aaa.length - 1 - i, s2 = aaa[s2Index];
aaa[s1Index] = s2;
aaa[s2Index] = s1;
}
for ( int i = 0 ; i < aaa.length; i ++ )
result[position][i] = aaa[i];
position ++ ;
}
return result;
}
private static boolean isDesending( int [] aaa) {
// 判断序列aaa是否为降序
for ( int i = 0 ; i < aaa.length - 1 ; i ++ )
if (aaa[i] < aaa[i + 1 ])
return false ;
return true ;
}
public static int [][] getArrange( int a) {
// 输出1到a的排列
int [][] result = new int [Fact(a)][a]; // 共Fact(a)个结果,结果的每个数存在第二维中
if (a == 1 )
result[ 0 ][ 0 ] = 1 ;
else // 递归的从下往上构造
{
int [][] temp = getArrange(a - 1 ); // 得到a-1时的结果
int [] aaa = new int [a - 1 ];
int position = 0 ; // result当前可用位置
for ( int i = 0 ; i < temp.length; i ++ ) {
aaa = temp[i]; // 依次取出每个结果
// 对这些结果做插入操作就得到了新的一个排列
for ( int j = 0 ; j < a; j ++ ) // 插入的不同位置决定了一个结果
{
result[position][j] = a;
// 将aaa的位放入剩下的几个位置
for ( int k = 0 , s = 0 ; k < a && s < a - 1 ; k ++ )
if (result[position][k] != a) {
result[position][k] = aaa[s];
s ++ ;
}
position ++ ;
}
}
}
return result;
}
public static int [][] JohnsonTrotter( int a) {
// Johnson-Trotter算法实现排列
int [][] result = new int [Fact(a)][a];
// 得到第一个排列及方向的初始化
int [] direct = new int [a]; // 方向,0指向左,1指向右边
for ( int i = 0 ; i < a; i ++ )
result[ 0 ][i] = i + 1 ;
int position = 1 ; // result下一个可用位置
// int[] aaa = result[0]; // 非常典型的错误!!!aaa此时和result[0]指向同一个引用,最周会导致result[0]是最后一个aaa的值
int [] aaa = new int [a];
for ( int i = 0 ; i < a; i ++ )
aaa[i] = result[ 0 ][i];
while (hasMove(aaa, direct)) {
int maxMove = maxMove(aaa, direct); // 求当前最大移动元素
int indexMax = 0 ; // 最大元素的下标
for ( int i = 0 ; i < aaa.length; i ++ )
if (aaa[i] == maxMove)
indexMax = i;
// 把k和它箭头指向的相邻元素互换
if (direct[indexMax] == 0 ) {
// 元素互换
int temp = aaa[indexMax];
aaa[indexMax] = aaa[indexMax - 1 ];
aaa[indexMax - 1 ] = temp;
// 相应的direct数组也要改变!!!
direct[indexMax] = direct[indexMax - 1 ];
direct[indexMax - 1 ] = 0 ;
} else if (direct[indexMax] == 1 ) {
int temp = aaa[indexMax];
aaa[indexMax] = aaa[indexMax + 1 ];
aaa[indexMax + 1 ] = temp;
// 相应的direct数组也要改变!!!
direct[indexMax] = direct[indexMax + 1 ];
direct[indexMax + 1 ] = 1 ;
}
// 调转所有大于k的元素的方向
for ( int i = 0 ; i < aaa.length; i ++ )
if (aaa[i] > maxMove) {
if (direct[i] == 0 )
direct[i] = 1 ;
else
direct[i] = 0 ;
}
// 将新排列加入到结果列表
for ( int i = 0 ; i < aaa.length; i ++ )
result[position][i] = aaa[i];
position ++ ;
// 错误同上,引用问题!!!这样会导致每个position的值实际上是一直在跟踪aaa
// result[position] = aaa;
// position++;
}
return result;
}
private static int maxMove( int [] a, int [] direct) {
// 求最大移动元素
int max = 0 ;
for ( int i = a.length - 1 ; i >= 0 ; i -- ) {
if (direct[i] == 0 && i != 0 )
if (a[i] > a[i - 1 ] && a[i] > max)
max = a[i];
if (direct[i] == 1 && i != a.length - 1 )
if (a[i] > a[i + 1 ] && a[i] > max)
max = a[i];
}
return max;
}
private static boolean hasMove( int [] a, int [] direct) {
// 判断序列a是否存在可移动元素
for ( int i = a.length - 1 ; i >= 0 ; i -- ) {
if (direct[i] == 0 && i != 0 )
if (a[i] > a[i - 1 ])
return true ;
if (direct[i] == 1 && i != a.length - 1 )
if (a[i] > a[i + 1 ])
return true ;
}
return false ;
}
private static int Fact( int a) {
// 求a的阶乘
int fact = 1 ;
while (a != 0 ) {
fact = fact * a;
a -- ;
}
return fact;
}
}
运行结果(生成1到4的排列):
4
减1治自底向上生成排列的算法:
4321 3421 3241 3214 4231 2431 2341 2314 4213 2413 2143 2134 4312 3412 3142 3124 4132 1432 1342 1324 4123 1423 1243 1234
Johnson-Trotter算法通过移动来生成排列:
1234 1243 1423 4123 4132 1432 1342 1324 3124 3142 3412 4312 4321 3421 3241 3214 2314 2341 2431 4231 4213 2413 2143 2134
字典序来构造排列的算法:
1234 1243 1324 1342 1423 1432 2134 2143 2314 2341 2413 2431 3124 3142 3214 3241 3412 3421 4123 4132 4213 4231 4312 4321
--------------------------------------------------------------------------------------------------------------------------------------------------
总结:
算法的思路上面的描述都很清晰了,但实现起来其实还不是很容易。
这些代码是十几天前写的了,还要好好看看具体的实现。