这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战
字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]
限制:
1 <= s 的长度 <= 8
题解
算法一:DFS回溯(Java)
本题是回溯算法经典题目,求全排列+去重
这道题目和全排列的区别在与给定一个可包含重复数字的序列,要返回所有不重复的全排列。
这里又涉及到去重了。
那么排列问题其实也是一样的套路。
还要强调的是去重一定要对元素经行排序,这样我们才方便通过相邻的节点来判断是否重复使用了。
对于一个长度为 n 的字符串(假设字符互不重复),其排列方案数共有:
根据字符串排列的特点,考虑深度优先搜索所有排列方案。即通过字符交换,先固定第 1 位字符( n 种情况)、再固定第 2 位字符( n-1 种情况)、... 、最后固定第 n 位字符( 1 种情况)。
当字符串存在重复字符时,排列方案中也存在重复的排列方案。为排除重复方案,需在固定某位字符时,保证 “每种字符只在此位固定一次” ,即遇到重复字符时不交换,直接跳过。从 DFS 角度看,此操作称为 “剪枝” 。
class Solution {
public String[] permutation(String s) {
if (s == null || s.length() == 0) {
return new String[0];
}
List<String> res = new ArrayList<>();
StringBuilder sb = new StringBuilder();
char[] chars = s.toCharArray();
boolean[] visited = new boolean[chars.length];
Arrays.sort(chars);
permutation2Helper(res, sb, chars, visited);
return res.toArray(new String[res.size()]);
}
private void permutation2Helper(List<String> res, StringBuilder sb, char[] chars, boolean[] visited) {
if (sb.length() == chars.length) {
res.add(String.valueOf(sb));
}
for (int i = 0; i < chars.length; i++) {
if (visited[i]) {
continue;
}
if (i > 0 && chars[i] == chars[i - 1] && !visited[i - 1]) {
continue;
}
visited[i] = true;
sb.append(chars[i]);
permutation2Helper(res, sb, chars, visited);
sb.deleteCharAt(sb.length() - 1);
visited[i] = false;
}
}
}
时间复杂度:O(N*N!)
空间复杂度:O(N)
算法二:DFS回溯(Go)
思路同上
func permutation(s string) (ans []string) {
t := []byte(s)
sort.Slice(t, func(i, j int) bool { return t[i] < t[j] })
n := len(t)
perm := make([]byte, 0, n)
vis := make([]bool, n)
var backtrack func(int)
backtrack = func(i int) {
if i == n {
ans = append(ans, string(perm))
return
}
for j, b := range vis {
if b || j > 0 && !vis[j-1] && t[j-1] == t[j] {
continue
}
vis[j] = true
perm = append(perm, t[j])
backtrack(i + 1)
perm = perm[:len(perm)-1]
vis[j] = false
}
}
backtrack(0)
return
}
时间复杂度:O(N*N!)
空间复杂度:O(N)