全排列问题
给出1~n
,n个整数,把这n个整数按照这种顺序输出n个数的所有排列:
- 按照字典序从小到大顺序输出,如果说
(a1,a2,……an)
的字典序小于(b1,b2,……bn)
,指的是存在一个i
,使得a1=b1,a2=b2,……a(i-1)=b(i-1),ai<bi
- 例如:
1,2,3
三个数按顺序输出全排列就是(1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,1,2)、(3,2,1)
解析
以输入的是1,2,3
为例,这个问题可以分解成这样的几个小问题:
拿出其中一个问题,以1
开头的三位全排列内部可以分成三个小问题,这三个小问题其实就是在1
为开头的基础上,判断第二位后面的各个位
因为1
已经在第一位出现过了,所以就不会出现在第二位,那么第二位只有两小小个问题
如此递推下去,这就是这道题的解决思想
那么我们可以设置一个boolean
类型的HashTable
,用来记录到目前为止,前几位哪些数字已经使用了,已经使用的记为true
设置一个index
,指示当前是第几位
假设我们在第一位为2
,第二位为1
的基础上到了第三位,第三位只能是三3
,到了这一步,这一种情况已经走到底了,hashTable
都为true
,
之后返回到第二位,第一位的2
我们不能动,第二位还可以是3
,但是由于刚才我们在第三位用到了3
,hashTable[3]
已经是true
了,第二位无法变成3
,这就说明,我们在返回到上这一位之后,要把子问题中变成true
的变回false
代码实现
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Main{
// 参与全排列的数字为1~n,同时也是位数
static int n;
// hashTable表示 数字i 在前几位中是否已经用过了
// 0位空着不用,这样更方便理解
static boolean[] hashTable = new boolean[n+1];
// 存放当前排列的结果,从i=1开始,0位空着不用,这样更方便理解
static int[] P = new int[n+1];
// index表示当前处理的是哪一位
public static void generateP(int index) {
// 如果当前位数是第n位(index是从1开始的),说明已经排完了
if(index == n+1) {
// 输出整个排列
for(int i=1; i<n; i++) {
System.out.print(P[i]);
}
System.out.println();
}
// 对于1~n的每一个数,都尝试放在第index位(当前位)上
for(int i=1; i<=n; i++) {
if(hashTable[i] == false) { // 数字i在前几位中没有出现过
//i设为全排列的第index位
P[index] = i;
// 把i标记成已经用过了
hashTable[i] = true;
// 处理子问题(下一位往后
generateP(index + 1);
// 处理完子问题,把i标记还原,以保证当前位位可以使用
hashTable[i] = false;
}
}
}
public static void main(String[] args) throws IOException{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
// 参与全排列的数字为1~n,同时也是位数
n = Integer.parseInt(bf.readLine());
// 从第一位开始
generateP(1);
}
}
参考:胡凡《算法笔记》