什么是字符串的全部排列
比如字符串String s = “abc”,那么它的全部排列就是“abc”、“acb”、“bac”、“bca”、“cab”、“cba”。
其实就是字符串中所有字符在每个位置的排列组合。
和练习一中的区别在于,根据字符串转换出来的char[] arr中的每个元素都不可以丢弃。所以这个练习中会用一种叫做“恢复现场”的手段来处理。
整体的思路大致是:
- 因为要看每个字符在字符串中不同位置的排列组合,所以第一步还是将String转换成char[]。
- 所以base case同样是当遍历到数组结尾时,将拼接的存储着路径结果的path结果添加到List中。
- 遍历char[],获取当前位置的字符,并在集合中移除它,向下递归传递,等递归结束后,再添加回集合中。
代码
public static List<String> permutation1(String str) {
List<String> ans = new ArrayList<>();
if (str == null || str.length() == 0) {
return ans;
}
char[] charArr = str.toCharArray();
List<Character> charList = new ArrayList<>();
for (char cur : charArr) {
charList.add(cur);
}
String path = "";
process(charList, path, ans);
return ans;
}
public static void process(List<Character> chars, String path, List<String> ans) {
if (chars.isEmpty()) {
ans.add(path);
return;
} else {
int len = chars.size();
for (int i = 0; i < len; i++) {
char cur = chars.get(i);
chars.remove(i);
process(chars, path + cur, ans);
chars.add(cur);
}
}
}
其中在遍历的过程中chars.remove(i)这行代码后,递归向下调用,进行拼接,如果递归执行完,不把remove的字符加回来。会有问题!!
- 代码程序报错,因为递归过程中遍历的集合的长度改变了。
- 就算代码程序不报错,不加回来,等到我遍历 i = 2时,cur = b字符,我集合中的字符越来越少了,得不到正确答案了。
第二种解题思路:交换
整体的过程是这样的,遍历的过程中,两两进行交换,直到到达数组长度后,证明没有可换的,同样是将结果放入List中,但是需要注意的是,交换完后,同样要换回来,要不然下次的遍历字符串的顺序就变了。
代码
比如说,String s = “abc”,第一次遍历时,每个位置都跟自己进行交换,但是当 i = 2,index = 1时,
bc进行交换, 而后得到字符串 acb,将结果放入到List中,如果得到结果后,不将bc位置换回来,下次就是获取字符串“acb”的全部排列了。所以这就是恢复现场。
public static List<String> permutation2(String str) {
List<String> ans = new ArrayList<>();
if (str == null || str.length() == 0) {
return ans;
}
char[] charArr = str.toCharArray();
process2(ans, 0, charArr);
return ans;
}
public static void process2(List<String> ans, int index, char[] chars) {
if (index == chars.length) {
ans.add(String.valueOf(chars));
return;
} else {
int len = chars.length;
for (int i = index; i < len; i++) {
swap(chars, index, i);
process2(ans, index + 1, chars);
swap(chars, index, i);
}
}
}
public static void swap(char[] chars, int i, int j) {
char tmp = chars[i];
chars[i] = chars[j];
chars[j] = tmp;
}
相同结果去重
如果此时再增加一个需要要将同样排列结果的字符串进行去重呢?String s = “cac”,因为有2个c,所以获取的全排列的结果中是有重复的值。
代码
只需要改变这个方法即可,根据ASCII的范围来确定boolean[]的大小,将char转换成ASCII的数值记录再boolean[]对应的下标处,如果为true,证明碰见过相同的字符,那就不在处理,当然,用Set也是可以的,不过最后处理时还要再次遍历一次Set,而用boolean,则更加的简便,如果判断不通过,则直接跳过。
public static void process2(List<String> ans, int index, char[] chars) {
if (index == chars.length) {
ans.add(String.valueOf(chars));
return;
} else {
int len = chars.length;
boolean[] visits = new boolean[256];
for (int i = index; i < len; i++) {
if (!visits[chars[i]]){
visits[chars[i]] = true;
swap(chars, index, i);
process2(ans, index + 1, chars);
swap(chars, index, i);
}
}
}
}