排列组合 & 回溯
前言
排列组合必须要枚举所有情况,但当其中有重复字符时,就会出现重复的组合,即同一位置上选择位置不同但字符相同时,则后面的一套组合都是重复的。
一、排列组合
二、回溯排列 & 两种去重
1、排序+pre去重
// 有重复字符的字符串排列组合。
public class Permutation {
/*
如何排列?回溯
如何去重?为什么重?
当回溯时,取前一个后后一个放到第i位时,且当前后相同,则会产生一套重复的排列组合;
通过记录上一个选择的pre字符和当前字符比较,如果相等则略过,从而防止重复的一套排列组合。
*/
public String[] permutation(String S) {
arr = S.toCharArray();
n = arr.length;
//bug3:想用pre去重,那就得前后排序,让相等的字符挨在一起,否则随着pre的改变,还是会让相同位置有重复。
Arrays.sort(arr);
dfs(new int[n]);
return ans.toArray(new String[0]);
}
// bug1:第二个参数用的HashSet<Character> pool,想让hashSet去重,那排列里面用于没有相同字符,这里需要对位置去重,采用数组hash。
private void dfs(int[] fx) {
if (sb.length() == n) {
ans.add(sb.toString());
return;
}
char pre = '@';// bug2:pre是用于区分当前位置的重复使用,不是前后。
for (int i = 0; i < n; i++) {
if (fx[i] != 1 && pre != arr[i]) {
fx[i] = 1;
sb.append(arr[i]);
dfs(fx);
sb.deleteCharAt(sb.length() - 1);
fx[i] = 0;
pre = arr[i];
}
}
}
char[] arr;
int n;
List<String> ans = new ArrayList<>();
StringBuilder sb = new StringBuilder();
/*
review:
bug1:用hashSet去重复字符串,其实我们要的是每个位置上的字符是否被重复选择;
bug2:pre当作参数传递,用来去前后字符是否选择重复,其实我们要的是当前位置前后使用是否重复。
bug3:想用pre去重,那就得前后排序,让相等的字符挨在一起,否则随着pre的改变,还是会让相同位置有重复。
总结:两处bug都有相同的本质,就是我思考时不够严谨,记忆容易模糊,让编码带上我以为的味道。
1-逻辑训练太少,逻辑不够严谨!该类题的逻辑训练太少。
2-抽象能力(看本质/去其糟糠取其精华)还需要提高,因为bug的总结中老是出现 我以为/其实我们要/我们却选择了等字眼。
*/
}
2、set去重
// 不排序剪枝,而是用set对最后的结果去重。
class Permutation2 {
/*
如何排列?回溯
如何去重?为什么重?
当回溯时,取前一个后后一个放到第i位时,且当前后相同,则会产生一套重复的排列组合;
通过组合的最终字符,看以前是否有组合过。
*/
public String[] permutation(String S) {
arr = S.toCharArray();
n = arr.length;
dfs(new int[n]);
return ans.toArray(new String[0]);
}
// bug1:第二个参数用的HashSet<Character> pool,想让hashSet去重,那排列里面用于没有相同字符,这里需要对位置去重,采用数组hash。
private void dfs(int[] fx) {
if (sb.length() == n) {
if (!pool.contains(sb.toString())) {
ans.add(sb.toString());
pool.add(sb.toString());
}
return;
}
for (int i = 0; i < n; i++) {
if (fx[i] != 1) {
fx[i] = 1;
sb.append(arr[i]);
dfs(fx);
sb.deleteCharAt(sb.length() - 1);
fx[i] = 0;
}
}
}
char[] arr;
int n;
List<String> ans = new ArrayList<>();
Set<String> pool = new HashSet<>();
StringBuilder sb = new StringBuilder();
}
总结
1)逻辑训练太少,逻辑不够严谨!该类题的逻辑训练太少。
2)抽象能力(看本质/去其糟糠取其精华)还需要提高,因为bug的总结中老是出现 我以为/其实我们要/我们却选择了等字眼。
3)分析问题并拆解,如:怎么排列–回溯;怎么去重?为什么会重复?