不得不说的全排列算法递归实现
我真的是菜啊,留的算法作业几乎没有一次自己写出来过…都是要上网看别人的博客才能懂,自己好笨好菜,😭😭😭
一个序列{a,b,c},求这个序列的全排列呢。相信大家动下膝盖就能想到是这个答案。
那么序列{a,b,c,d}呢,它的全排列是什么我不知道,但是我知道所有以d开头的排列方式。
眼熟就对了,他就是abc的全排列开头加上d。
这样就很清楚了,每个字母开头,后面都是剩余字母的全排列。{a,b,c}中以a开头,后面就是bc的全排列,第二个字母后面又是剩下的所有字母的全排列。
这样一直下去就形成了递归,直到最后一个字母,就像下图。
这里说下我最初的实现想法,既然每次都要挑出一个字母,那么剩下的字母就可以形成一个新序列,然后再挑出一个字母,剩下的又能组成一个新序列,就这样一直到最后一个字母,原理就是不断的进行Cn1。
但是这样就有一个问题,当我取到最后一个字母,形成了一个全排列其中之一,我又要去生成下一个排列啊。那我要怎么还原它呢???
只能再次求助万能的某度,找了个大家的方法。
总体思想通过数组指针和交换元素顺序来不断缩小序列范围和确定选出的元素。
在这里举一个b开头的栗子🌰,有点丑希望大家不要介意。
至于怎么还原,就等每次某个字母后的所有全排列都形成后,重新交换回位置即可。
因为每个字母都有可能被取出来,这个跟广度有关,所以在递归的外面要用for循环包裹,一直到能选到最后一个字母。
public static void swap(char[] chars, int i, int j) {
char tmp = chars[i];
chars[i] = chars[j];
chars[j] = tmp;
}
/*
* @Title allPermutation
* @Description chars的全排列
* @author 滑技工厂
* @Date 2020/2/27
* @param [chars, cursor, end]
* @return void
* @throws
*/
public static void allPermutation(char[] chars, int cursor, int end) {
if (cursor == end) {//cursor到数组最末
System.out.println(Arrays.toString(chars));
}
for (int i = cursor; i <= end; i++) {
//
swap(chars, cursor, i);
//指针后移,cursor之前的都是已经确定好的元素,+1来确定下一个位置的字母
allPermutation(chars, cursor + 1, end);
//用来把之前交换的两个值换回来
swap(chars, cursor, i);
}
}
但是这个方法只对于不重复序列有效,如果有重复序列则会重复输出相同的排列。就要在每次swap之前添加一个判断是否相同的判断,相同则continue,不同则继续执行。
import java.util.Arrays;
/**
* @ClassName Permutation
* @Description 全排列类
* @author 滑技工厂 https://blog.csdn.net/qq_41718454
* @date 2020/2/27
* @version 1.0
*/
public class Permutation {
public static void swap(char[] chars, int i, int j) {
char tmp = chars[i];
chars[i] = chars[j];
chars[j] = tmp;
}
/*
* @Title allPermutation
* @Description chars的全排列
* @author 滑技工厂
* @Date 2020/2/27
* @param [chars, cursor, end]
* @return void
* @throws
*/
public static void allPermutation(char[] chars, int cursor, int end) {
if (cursor == end) {//cursor到数组最末
System.out.println(Arrays.toString(chars));
}
for (int i = cursor; i <= end; i++) {
if (!swapAccepted(chars, cursor,i))
continue;
//选一个元素放入已排列
swap(chars, cursor, i);
//指针后移,cursor之前的都是已经确定好的元素,+1来确定下一个位置的字母
allPermutation(chars, cursor + 1, end);
//用来把之前交换的两个值换回来
swap(chars, cursor, i);
}
}
public static boolean swapAccepted(char[] array, int start, int end) {
for (int i = start; i < end; i++) {
if (array[i] == array[end]) {
return false;
}
}
return true;
}
public static void main(String[] args) {
char[] chars = {'a', 'b', 'c', 'c'};
allPermutation(chars, 0, chars.length - 1);
}
}
总结
核心还是要想到新增加一个元素的全排列是建立在原元素的全排列的基础上的,这样想的话不论多长的序列最终都将变成一个元素的排列。
这里要感谢这篇帖子:全排列算法的全面解析
这篇看题到完成也写了一天了,如果你觉得对你有帮助就点个小赞👍吧。
你的鼓励是对我的最大支持!