上一篇博文 数学问题(1) — 组合问题的程序实现 中讲解的组合问题的算法实现。问题的背景差不多,废话不多说,接着来讲排列问题的实现。排列的算法叫:“全排列——邻位对换法”
1、根据上一篇博文 数学问题(1) — 组合问题的程序实现 先得到组合情况,再遍历每一种组合的排列。一个[1,2…n]的一个排列,其上每一个整数都给了一个方向, 如果它的箭头所指的方向的邻点小于它本身,我们称整数k是可移的。
2、显然1永远不可移,n除了以下两种情形外,它都是可移的: (1) n是第一个数,且其方向指向左侧 (2) n是最后一个数,且其方向指向右侧
3、我们可按如下算法产生所有排列: 1,开始时:存在排列123…n,除1外,所有元素均可移,即方向都指向左侧。
4、当最大元素n可移动时,将其从一端依次移动到另一端,即可得到n-1个全排列;
5、当n移动到某一端后,不能再移动,此时寻找最大的可移数m,将m与其箭头所指的邻数互换位置,这样就又得到一个新的全排列;
6、将所得新排列中所有比m大的数p的方向调整,即改为相反方向,这样使得n又成了可移数。 3,重复第2步直到所有的元素都不能移动为止。
举例:数组 ["红", "绿", "蓝","黄"]
------4中选3的排列的种类总数有:24---------
红 绿 蓝
红 蓝 绿
蓝 红 绿
蓝 绿 红
绿 蓝 红
绿 红 蓝
红 绿 黄
红 黄 绿
黄 红 绿
黄 绿 红
绿 黄 红
绿 红 黄
红 蓝 黄
红 黄 蓝
黄 红 蓝
黄 蓝 红
蓝 黄 红
蓝 红 黄
绿 蓝 黄
绿 黄 蓝
黄 绿 蓝
黄 蓝 绿
蓝 黄 绿
蓝 绿 黄
代码:
import java.util.ArrayList;
import java.util.List;
/**
* 排列算法
*
* @author zkd
* @Date 2014/5/11
*/
public class Permutation {
//打印n取m的排列所有情况
public void diaplayPermutation(String[] object, int n, int m) {
System.out.println("------" + n + "中选" + m + "的排列的种类总数有:"+countOfPermutation(n,m)+"---------");
List<String[]> list = PermutationObject(object, n, m);
for (int i = 0; i < list.size(); i++) {
String[] sa = list.get(i);
String s = "";
for (int j = 0; j < sa.length; j++) {
s += (" " + sa[j]);
}
System.out.println(s);
}
}
//对一个组合情况进行全排列 返回总排列种数
private List<String[]> PermutationObject(String[] object, int n, int m) {
List<String[]> result = new ArrayList<>();//存储总的排列结果
List<String[]> cob = generateCombinationObject(object, n, m);//取得组合情况
List<int[]> pai = fullPermutationSequence(m);//取得全排列序列
//对每个组合用全排列序列进行排列
for (int i = 0; i < cob.size(); i++) {
String[] str = cob.get(i);
for (int j = 0; j < pai.size(); j++) {
int[] lie = pai.get(j);
String[] s = new String[m];
int t = 0;
for (int k = 0; k < lie.length; k++,t++) {
s[t] = str[lie[k]-1];
}
result.add(s);
}
}
return result;
}
//返回全排列序列
public List<int[]> fullPermutationSequence(int n) {
List<int[]> list = new ArrayList<>();
int[] a = new int[n];
boolean[] p = new boolean[n];
for (int i = 0; i < n; i++) {
a[i] = i + 1;
p[i] = false;
}
do {
list.add(a.clone());
if (n == a[n - 1]) {
for (int i = n - 1; i > 0; i--) {
int temp = a[i];
a[i] = a[i - 1];
a[i - 1] = temp;
boolean f = p[i];
p[i] = p[i - 1];
p[i - 1] = f;
list.add(a.clone());
}
} else {
for (int i = 1; i < n; i++) {
int temp = a[i];
a[i] = a[i - 1];
a[i - 1] = temp;
boolean f = p[i];
p[i] = p[i - 1];
p[i - 1] = f;
list.add(a.clone());
}
}
} while (canMove(a, p, n));
return list;
}
//换位法判断能否移位 此过程也有可能产生一种排列,判断后要存储
private boolean canMove(int[] a, boolean[] p, int n) {
int max = 1;
int pos = -1;
for (int i = 0; i < n; i++) {
if (a[i] < max) {
continue;
}
if ((p[i] && (i < n - 1) && (a[i] > a[i + 1])) || // 指向右侧
(!p[i] && (i > 0) && (a[i] > a[i - 1]))) // 指向左侧
{
max = a[i];
pos = i;
}
}
if (pos == -1) // 都不能移动
return false;
// 与其箭头所指的邻数互换位置
if (p[pos]) // 指向右侧
{
int temp = a[pos];
a[pos] = a[pos + 1];
a[pos + 1] = temp;
boolean flag = p[pos];
p[pos] = p[pos + 1];
p[pos + 1] = flag;
} else // 指向左侧
{
int temp = a[pos];
a[pos] = a[pos - 1];
a[pos - 1] = temp;
boolean flag = p[pos];
p[pos] = p[pos - 1];
p[pos - 1] = flag;
}
// 将所得排列中所有比max大的数p的方向调整
for (int i = 0; i < n; i++) {
if (a[i] > max)
p[i] = !p[i];
}
return true;
}
//根据组合序列返回组合结果集
private List<String[]> generateCombinationObject(String[] object, int n,
int m) {
List<int[]> list = generateCombination(n, m);
List<String[]> result = new ArrayList<>();
int j, k;
int[] a;
for (int i = 0; i < list.size(); i++) {
a = list.get(i);
String[] s = new String[m];
k = 0;
for (j = 0; j < a.length; j++) {
if (1 == a[j]) {
s[k] = object[j];
if (k < m - 1) {
k++;
}
}
}
result.add(s);
}
return result;
}
//返回n选m的组合情况序列
private List<int[]> generateCombination(int n, int m) {
List<int[]> list = new ArrayList<>();
int[] tempNum = new int[n];
boolean flag; // 判断算法的结束
// 初始化数组,同时得到第一组组合系列
for (int i = 0; i < n; i++) {
if (i < m) {
tempNum[i] = 1;
} else {
tempNum[i] = 0;
}
}
int[] b = tempNum.clone();
list.add(b);
if (n == m) {
return list;
}
do {
int pose = 0; // 记录改变的位置
int sum = 0; // 记录改变位置 左侧 1 的个数
// 然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为“01”
for (int i = 0; i < (n - 1); i++) {
if (tempNum[i] == 1 && tempNum[i + 1] == 0) {
tempNum[i] = 0;
tempNum[i + 1] = 1;
pose = i;
break;
}
}
// 同时将其左边的所有“1”全部移动到数组的最左端。
for (int i = 0; i < pose; i++) {
if (tempNum[i] == 1)
sum++;
}
for (int i = 0; i < pose; i++) {
if (i < sum)
tempNum[i] = 1;
else
tempNum[i] = 0;
}
// 判断是否为最后一个组合:当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得到了最后一个组合。
flag = false;
for (int i = n - m; i < n; i++) {
if (tempNum[i] == 0)
flag = true;
}
int[] a = tempNum.clone();
list.add(a);
} while (flag);
return list;
}
public int countOfPermutation(int n,int m){
int sum=1;
for(int i=n;i>=(n-m+1);i--){
sum*=i;
}
return sum;
}
public static void main(String[] args) {
Permutation p = new Permutation();
String[] object2 = { "红", "绿", "蓝","黄" };
p.diaplayPermutation(object2, 4, 3);
}
}