Java递归求全排列详解
全排列的递归思想解释:
全排列的数学定义就不再过多解释,考虑递归算法的实现可从下面几点入手(以数组为例,如对其他元素排列,将元素编号放入数组即可):
1、一个数的全排列,如排列{1},就是这个数本身这一种情况
2、两个数的全排列,如排列{1,2}:
第一步:将{1}放在第零个位置,剩下的{2}进行一个数的全排列,结果为{1,2}
第二步:将{2}放在第零个位置,剩下的{1}进行一个数的全排列,结果为{2,1}
即两个数的全排列为以上2种情况。
3、三个数的全排列,如排列{1,2,3}:第一步:将{1}放在第零个位置,剩下的{2,3}进行两个数的全排列,结果为{1,2,3} {1,3,2}
第二步:将{2}放在第零个位置,剩下的{1,3}进行两个数的全排列,结果为{2,1,3} {2,3,1}
第三步:将{3}放在第零个位置,剩下的{1,2}进行两个数的全排列,结果为{3,1,2} {3,2,1}即三个数的全排列为以上6种情况。
4、即m个数(无重)的全排列,就是将m个数分别放在第零个位置,再将剩下的m-1个数的全排列加在后面,当m-1=1时为递归>的出口。
代码
注意点
- 一个数的全排列就是其本身
程序的主要思路是:
把第1个数换到最前面来(本来就在最前面),准备打印1xx,再对后两个数2和3做全排列。
把第2个数换到最前面来,准备打印2xx,再对后两个数1和3做全排列。
把第3个数换到最前面来,准备打印3xx,再对后两个数1和2做全排列。
可见这是一个递归的过程,把对整个序列做全排列的问题归结为对它的子序列做全排列的问题
解题过程:
(1) 当 N = 1的时候,则直接打印数列即可。
(2) 当 N = 2的时候,设数组为 [a, b]
打印a[0], a[1] (即a,b)
交换a[0],a[1]里面的内容
打印a[0],a[1] (此时已变成了 [b, a] )
(3) 当 N = 3的时候,数组为 [a, b, c]
把a放在 a[0] 的位置(原本也是如此,a[0] = a[0]),打印b,c的全排列(即a[1], a[2]的全排列)
a b c
a c b
把b放在a[0]的位置(这时候需要交换原数组的a[0]和a[1]),然后打印a, c的全排列
b a c
b c a
打印完后再换回原来的位置,即a还是恢复到a[0],b还恢复到a[1]的位置
把c放在a[0]的位置(这时候需要交换的是原数组的a[0]和a[2]),然后打印a, b的全排列
c b a
c a b
打印完后再换回原来的位置,即a还是恢复到a[0],b还恢复到a[1]的位置,至此,全排列完成
当 N = 4,5,6,……的时候,以此类推。
//全排列函数(递归),参数k是确定第k个位上的字符
//k是数组元素下标,从0开始,所以递归终止条件中arr.length可以不减1,结果一样的
private static void f(char[] arr, int k) {
if (k == arr.length-1) {
System.out.println(arr);
}
for (int i = k; i < arr.length; i++) {
//交换位置
char t = arr[k];
arr[k] = arr[i];
arr[i] = t;
f(arr, k+1);
//回溯
t = arr[k];
arr[k] = arr[i];
arr[i] = t;
}
}
无重复元素测试
public class Test{
public static void main(String[] args) {
char[] cArr={'A','B','C'};
f(cArr,0);
}
//全排列函数(递归),参数k是确定第k个位上的字符
//k是数组元素下标,从0开始,所以递归终止条件中arr.length可以不减1,结果一样的
private static void f(char[] arr, int k) {
if (k == arr.length-1) {
System.out.println(arr);
}
for (int i = k; i < arr.length; i++) {
char t = arr[k];
arr[k] = arr[i];
arr[i] = t;
f(arr, k+1);
//回溯
t = arr[k];
arr[k] = arr[i];
arr[i] = t;
}
}
}
运行结果
有重复元素测试结果
使用AAB测试,结果有重复项
如何去重?
可以在全局new一个set(注意使用static),然后每次把全排列的结果add到set里就可以了