本质
解决全排列问题本质是:二分法。
将某个数组的全排列看成是由第0个元素与其后所有元素的全排列组成。
做法是每次只需要挑选某个元素作为当前排列的第0个元素,然后剩下的变成子问题递归来解决。
原理
设一个数组的长度为n,则其全排列的个数为n!
个。
设f(n)代表对于一个n长度数组的全排列,array(n)代表数组中的第n个元素,+
代表拼接。
f(n) = array(1 to n) + f(n - 1)
由此可以看出这个算法需要利用递归来解决问题。每次只需要挑选某个元素作为第0个元素,然后剩下的变成子问题递归来解决。
例子:
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 2, 1]
[3, 1, 2]
我们可以从交换的角度和二分(大问题分解成小问题)的角度去思考。我们从交换的角度出发去思考应该如何交换才能形成全排列,从二分的角度思考如何划分子问题。
- 对于{1},这个排列只有其本身,于是可以看成自身与自身交换。
- 对于{1,2},排列为{{1, 2}, {2, 1}},从分的角度看:可以看成先选出第0个位置应该是哪个数+其后所有数的全排列。二分后就可以将这变成1的情况。
- 对于{1, 2, 3},同样是第0个元素可以是1或2或3,然后子问题变成2的情况。
题目
描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述: 输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
实现
import java.util.ArrayList;
import java.util.TreeSet;
public class Solution {
public ArrayList<String> Permutation(String str) {
TreeSet<String> r = new TreeSet<>();
DFS(r, str.toCharArray(), 0, str.length());
ArrayList<String> result = new ArrayList<>(r.size());
for (String s : r) {
result.add(s);
}
return result;
}
public static void DFS(TreeSet<String> result,char[] str, int begin, int end) {
if (begin + 1 == end) {
result.add(new String(str));
}
for (int i = begin; i < end; i++) {
swap(str, begin, i);
DFS(result, str, begin + 1, end);
swap(str, begin, i);
}
}
public static void swap(char[] array, int aPos, int bPos) {
char t = array[aPos];
array[aPos] = array[bPos];
array[bPos] = t;
}
}