全排列问题
全排列是一个典型的递归问题,可以当作递归学习的入门问题。不了解什么是全排列问题的点我
说在前面
@Test 注解用于单元测试,读者不用的话改成main函数即可
这些代码是我准备蓝桥杯刷的一些题,这些方法名是我根据具体代码的特点命名,仅供参考
1.前缀法
过程举例
我们需要对strs = {“a”, “b”, “c”}全排列
如果数据很小,我们可以根据自己的经验就可以得到结果,abc,acb,bac,bca,cab,cba。但是要想让计算机完成,我们就要告诉他明确的步骤。
前缀法指的是,初始结果s="", 即当前 前缀为空,我们开始从头到尾扫描strs, 如果strs[i] 不在s字符串里面,那我们就把s加到strs[i]后面,可以得到
s1="a", s2="b", s3="c" 。
这样我们就得到三个新的前缀.。
对于s1,我们再次从头到尾扫描strs, 如果strs[i] 不在s字符串里面,那我们就把s加到strs[i]后面,可以得到
s11="ab", s12="ac"
对于s11,我们再次从头到尾扫描strs, 如果strs[i] 不在s字符串里面,那我们就把s加到strs[i]后面,可以得到
s111="abc" 此时字符串长度等于strs的长度,这就是我们想要的结果之一啦
程序实现如下
static int acc20=0;
@Test // 全排列 前缀法
public void tset20() {
String[] strs = {"a", "b", "c", "d", "e"}; // 全排列的数组 也可以是字符串,对此方法影响不大
boolean[] jugs = new boolean[strs.length]; // 用于判断前缀相应位置是否存在对应字符
fun20(strs, jugs, "");
System.out.println(acc20);
}
/**
* @param strs 全排列的数组
* @param pro 存储前缀,最终结果的前一部分
* @param jugs 用于判断前缀相应位置是否存在对应字符
* 比如,pro="abcd" 那么jugs 只有jugs[4]=false,其他为true
*
*/
public static void fun20(String[] strs, boolean[] jugs, String pro) {
if (pro.length() == strs.length) {
System.out.println(pro); //此时字符串长度等于strs的长度 可以打印也可以存放到数组或者list容器中
acc20++;
}
for (int i=0; i<strs.length; i++) {
if (!jugs[i]) {
jugs[i] = true;
fun20(strs, jugs, pro+strs[i]);
jugs[i] = false; // 回溯 以免影响下次迭代
}
}
}
如果你对 jugs[i] = false; 语句产生疑问往下看
一个前缀字符串对应一个jugs判断数组,而且jugs 只有申请一次空间,所以所有的前缀共用同一个jugs
jugs[i]原本为false 如果你将jugs[i] = true;那么为了不影响下次别人使用需要将他还原
写博客很费时间 后面的三种方法,就请读者先按照strs = {“a”, “b”, “c”}或者数据更小的例子自己在演草纸上写一边过程,或者直接复制我的代码,然后一步一步调式。就行啦,这篇文章需要你对迭代有基础的了解,以后有时间我会写一遍迭代的文章。
插入法
@Test // 全排列 递归插入法
public void tset19() {
String[] strs = {"a", "b", "c", "d", "e"};//, "c", "d", "e"};
List<String> list = fun19(strs, strs.length-1);
System.out.println();
}
/**
*
* @param strs
* @param n 表示当前考虑的插入元素为 strs[n]
* @return
*/
public static List<String> fun19(String[] strs, int n) {
List<String> list2 = new LinkedList<>(); // 存储返回结果
if (n==0) {
list2.add(strs[0]);
return list2;
}
List<String> list1 = fun19(strs, n-1); // 获得上一级递归结果
for (int i=0; i<list1.size(); i++) {
String s = list1.get(i);
list2.add(strs[n]+s); // 插在右边
list2.add(s+strs[n]); // 插在左边
for (int j=0; j<s.length()-1; j++) { // 循环插在某二个字符的中间
String s2 = s.substring(0,j+1) + strs[n] + s.substring(j+1,s.length()); // strs[n]
list2.add(s2);
}
}
return list2;
}
交换法
这是我学会的第一种方法参考于阿珂
@Test // 全排列 交换法
public void tset018() {
String[] strs = {"a", "b", "c", "d", "e"};//, "c", "d", "e"};
fun18(strs, 0, strs.length);
}
/**
*
* @param strs
* @param star 字面意思
* @param end
*/
public static void fun18(String[] strs, int star, int end) {
if (star==end-1) {
System.out.println(Arrays.toString(strs));
}
for (int i=star; i<end; i++) {
swap18(strs, star, i);
fun18(strs, star+1, end);
swap18(strs, star, i);
}
}
public static void swap18(String[] strs, int a, int b) {
String tr = strs[a];
strs[a] = strs[b];
strs[b] = tr;
}
选择法
static int acc17=0;
@Test // 全排列 选择法
public void tset017() {
String[] strs = {"a", "b", "c", "d", "e"};//, "c", "d", "e"};
boolean[] jugs = new boolean[strs.length];
fun17(strs, "", jugs, 0);
System.out.println(acc17);
}
/**
* @param strs 全排列的数组
* @param s 存放结果
* @param jugs 辅助
* @param core 考虑当前元素下标
*/
static void fun17(String[] strs, String s, boolean[] jugs, int core) {
if (core == strs.length){
System.out.println(s);
acc17++;
return;
}
for (int i=0; i< strs.length; i++) {
if (!jugs[i]) {
jugs[i] = true;
fun17(strs, s+strs[i], jugs, core+1);
jugs[i] = false;
}
}
}
如果你看完这四种方法后,找到了其中的共同之处那么恭喜你,再遇见迭代就不慌了