【正确答案】
【答案解析】问题描述:针对1、2、2、3、4、5这6个数字,写一个函数,打印出所有不同的排列,例如512234、215432等,要求“4”不能在第三位,“3”与“5”不能相连。
打印数组的排列组合方式最简单的方法就是递归,但本题存在两个难点:第一,数字中存在重复数字;第二,明确规定了某些位的特性。采用常规的求解方法似乎不能完全适用了。
其实,可以换一种思维,把求解这6个数字的排列组合问题转换为大家都熟悉的图的遍历问题,解答起来就容易多了。可以把1、2、2、3、4、5这6个点看作图的6个结点,对6个结点两两相连可以组成一个无向连通图,这6个数字对应的全排列等价于从这个图中各个结点出发深度遍历这个图所有可能路径所组成的数字集合,例如,从结点1出发的所有遍历路径组成了以1开头的所有数字的组合。由于“3”与“5”不能相连,因此在构造图时使图中3和5对应的结点不连通就可以满足这个条件。对于“4”不能在第三位,可以在遍历结束后判断是否满足这个条件。
具体而言,实现步骤如下所示。
1)用1、2、2、3、4、5这6个数字作为6个结点,构造一个无向连通图。除了“3”与“5”不连通外,其他所有结点都两两相连。
2)分别从这6个结点出发对图做深度优先遍历。每次遍历完所有结点,把遍历的路径对应数字的组合记录下来,若这个数字的第三位不是“4”,则把这个数字存放到集合Set中(由于这6个数中有重复的数,因此最终的组合肯定也会有重复的。由于集合Set的特点为集合中的元素是唯一的,不能有重复的元素,因此通过把组合的结果放到Set中可以过滤掉重复的组合)。
3)遍历Set集合,打印出集合中的所有结果,这些结果就是本问题的答案。
实现代码如下:
import java.util.*;
public class Test{
prirate int[]numbers=new int[]{1, 2, 2, 3, 4, 5};
private int n=numbers.length;
//用来标记图中结点是否被遍历过
private boolean[]visited=new boolean[n];
//图的二维数组表示
private int[][]graph=new int[n][n];
//数字的组合
private String combination="";
public Set<String>getAllCombinations(){
//构造图
buildGraph();
//用来存放所有组合
Set<String>set=new HashSet<String>();
//分别从不同的结点出发深度遍历图
for(int i=0; i<n; i++){
this.depthFirstSearch(i, set);
}
return set;
}
private void buildGraph(){
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
if(i==j){
graph[i][j]=0;
}else{
graph[i][j]=1;
}
}
}
//确保在遍历时3与5是不可达的
graph[3][5]=0;
graph[5][3]=0;
}
//对树从结点start位置开始进行深度遍历
private void depthFirstSearch(int start, Set<String>set){
visited[start]=true;
combination=combination+numbers[start];
if(combination.length()==n){
//4不出现在第三个位置
if(combination.indexOf("4")!=2)
set.add(combination);
}
for(int j=0; j<n; j++){
if(graph[start][j]==1&&visited[j]==false)
depthFirstSearch(j, set);
}
combination=combination.substring(0, combination.length()-1);
visited[start]=false;
}
public static void main(String[]args){
Test t=new Test();
Set<String>set=t.getAllCombinations();
Iterator<String>it=set.iterator();
while(it.hasNext()){
String string=(String)it.next();
System.out.println(string);
}
}
}
由于结果过多,这里就不列出详细的运行结果了。