关于排列组合的数学知识,推荐阅读
http://blog.csdn.net/onlyqi/article/details/8525280
[A, B, D, C]
[A, C, B, D]
[A, C, D, B]
[A, D, C, B]
[A, D, B, C]
[B, A, C, D] -- 然后是A和B交换,后面的元素递归调用。
[B, A, D, C]
[B, C, A, D]
[B, C, D, A]
[B, D, C, A]
[B, D, A, C]
[C, B, A, D]
[C, B, D, A]
[C, A, B, D]
[C, A, D, B]
[C, D, A, B]
[C, D, B, A]
[D, B, C, A]
[D, B, A, C]
[D, C, B, A]
[D, C, A, B]
[D, A, C, B]
[D, A, B, C]
我们应该牢记排列组合的数学公式,以便检验代码的输出是否正确。
在这里我把问题变化一下,可以得到3类问题:
- 打印m个字母的所有排列方式。我们常称这种排列方式为全排列,然而在数学上,称为置换substitution。
public static void subtitution(char[] s, int start, int end) {
if (start == end) {
System.out.println(Arrays.toString(s));
} else {
for (int i = start; i <= end; i++) {
char temp;
temp = s[start];
s[start] = s[i];
s[i] = temp;
subtitution(s, start + 1, end);
temp = s[i];
s[i] = s[start];
s[start] = temp;
}
}
}
从置换这个名字我们可以得到一些灵感:就是从数组的第一个元素开始,和后面的元素依次交换。
[A, B, C, D] -- 1,首先是A和自己交换 2,后面的元素递归调用 3,将之前交换后产生的数组复原[A, B, D, C]
[A, C, B, D]
[A, C, D, B]
[A, D, C, B]
[A, D, B, C]
[B, A, C, D] -- 然后是A和B交换,后面的元素递归调用。
[B, A, D, C]
[B, C, A, D]
[B, C, D, A]
[B, D, C, A]
[B, D, A, C]
[C, B, A, D]
[C, B, D, A]
[C, A, B, D]
[C, A, D, B]
[C, D, A, B]
[C, D, B, A]
[D, B, C, A]
[D, B, A, C]
[D, C, B, A]
[D, C, A, B]
[D, A, C, B]
[D, A, B, C]
最后输出的结果应该是n的阶乘。如上例为4*3*2*1=24种。大家每次做这种题时,应使用数学公式验证结果是否正确。
- 在m个字母中选择n个字母,并打印。注意这里是组合,也就是说123和132是一个。这个在数学上称为组合combination。
组合的数学公式是. 下面算法用数学公式检验是正确的。
import java.util.Stack;
public class App {
public static void main(String[] args) {
comb(new String[]{"A", "B", "C", "D", "E", "F", "G"}, 3);
}
public static void comb(String[] m, int n) {
Stack<String> buf = new Stack<String>();
doComb(m, 0, n, buf);
}
public static void doComb(String[] m, int start, int n, Stack<String> buf) {
if (m.length - start + buf.size() == n) {
for(String s : buf) {
System.out.print(s + ",");
}
for(int i = start; i < m.length; ++i) {
System.out.print(m[i] + ",");
}
System.out.println("");
}else if (start < m.length){
buf.push(m[start]);
doComb(m, start + 1, n, buf);
buf.pop();
doComb(m, start + 1, n, buf);
}
}
}
- 在m个字母中选择n个字母,并打印。这里我们变化一下,要的是排列,也就是说123和132不是一个。在数学上称为排列permutation.
现在我们就可以把问题分成两部分了:首先使用上面的方法二在m个字母中选择出n个,然后是用方法一对每种组合进行全排列就可以了。
import java.util.ArrayList;
import java.util.Stack;
public class App {
public static ArrayList<String> al = new ArrayList<String>();
public static void main(String[] args) {
comb(new String[] { "A", "B", "C", "D" }, 3);
}
public static void comb(String[] m, int n) {
Stack<String> buf = new Stack<String>();
ArrayList<String> al = new ArrayList<String>();
al = doComb(m, 0, n, buf);
for (String s : al) {
perm(s.toCharArray(),0,2);
}
}
public static ArrayList<String> doComb(String[] m, int start, int n,
Stack<String> buf) {
if (m.length - start + buf.size() == n) {
String x = "";
for (String s : buf) {
x = x + s;
}
for (int i = start; i < m.length; ++i) {
x = x + m[i];
}
al.add(x);
} else if (start < m.length) {
buf.push(m[start]);
doComb(m, start + 1, n, buf);
buf.pop();
doComb(m, start + 1, n, buf);
}
return al;
}
public static void perm(char[] buf, int start, int end){
if(start==end){
for(int i=0;i<=end;i++){
System.out.print(buf[i]);
}
System.out.println();
}
else{
for(int i=start;i<=end;i++){
char temp=buf[start];
buf[start]=buf[i];
buf[i]=temp;
perm(buf,start+1,end);
temp=buf[start];
buf[start]=buf[i];
buf[i]=temp;
}
}
}
}
最后,如果仅需要计算有多少种可能的组合,而不需要打印,那么不论是置换,排列还是组合都很简单。我们可以直接使用数学公式计算(公式参考本文开始提到的另外一篇blog),也可以使用递归(递归解法可以参考另外一篇文章:http://blog.csdn.net/onlyqi/article/details/7536150)
下面是用递归计算组合的代码。
public class Testing {
public static void main(String arg[]){
System.out.println(selector1(5,3));
}
public static int selector1(int i, int j){
if(j == 1){
return i;
}
if(i == j){
return 1;
}
return selector1(i-1,j-1)+selector1(i-1,j);
}
}
<pre>