剑指 Offer 38. 字符串的排列
思路: 递归回溯。
将字符串转为字符数组后,DFS搜索所有排序方案,即一次固定一个字母。
搜索时,通过swap操作来依序固定字母,得到一个排列方案,搜索后通过swap操作复原。
代码:
class Solution { Set<String> list = new HashSet<>(); char[] arr; public String[] permutation(String s) { arr = s.toCharArray(); dfs(0); return list.toArray(new String[0]); } void dfs(int index){ if (index == arr.length-1){ list.add(String.valueOf(arr)); return; } for (int i = index; i < arr.length; i++){ swap(index, i, arr);//将 i 固定在第 index 位, dfs(index+1);//固定第 index+1 位 swap(index, i, arr);//恢复 i 的位置 } } void swap(int i, int j, char[] c){ char tmp = c[i]; c[i] = c[j]; c[j] = tmp; } }
考虑到字符串中可能存在重复的字母,重复字母间交换顺序不会增加新的排序方案,因此可以加入剪枝操作。
使用HashSet记录已排序字母,如果下一个字母已在set中,则跳过。
修改后:
class Solution { Set<String> list = new HashSet<>(); char[] arr; public String[] permutation(String s) { arr = s.toCharArray(); dfs(0); return list.toArray(new String[0]); } void dfs(int index){ if (index == arr.length-1){ list.add(String.valueOf(arr)); return; } HashSet<Character> set = new HashSet<>();//新建HashSet记录已固定字母 for (int i = index; i < arr.length; i++){ if (set.contains(arr[i]))//剪枝 continue; set.add(arr[i]);//将即将固定的字母加入set swap(index, i, arr);//将 i 固定在第 index 位, dfs(index+1);//固定第 index+1 位 swap(index, i, arr);//恢复 i 的位置 } } void swap(int i, int j, char[] c){ char tmp = c[i]; c[i] = c[j]; c[j] = tmp; } }