题目
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = “abc”
输出:[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]
限制:
1 <= s 的长度 <= 8
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof
一、解题思路
在阐述思路前,我们先来看看回溯法。
解决一个回溯问题,实际上就是一个决策树的遍历过程。
你只需要思考三个问题:
- 路径
- 选择
- 结束条件
代码方面有一个模板:
res[];
public void dfs(参数){
if(满足条件){
res.add();
}
for(遍历选择列表){
做选择
dfs(参数)
撤销选择
}
}
核心就是for循环里的递归,递归前选择,递归后撤销
我们不妨称这为回溯法的决策树,因为每个节点都在做决策。
而回溯呢,就是在你决策之后回溯到决策前的状态,上面就是回溯法的大体理解。
回到这个题目来,我们先认为传入进来的字符串是不重复的。
字符串转char数组 char[] c:[a,b,c].
我们可以先固定第一位字符(n种情况),再固定第二位字符(n-1种情况),最后再固定第n个字符(1种情况)。
例如在固定第x位字符时,我们可以通过遍历数组
for(int i=x;i<c.length;i++) -----x为当前需要固定的字符的下标
依次令c[i]与c[x]交换,从而实现固定的第一位字符有n种情况
当然在固定好第一位字符后,会依次固定第二位,第三位,,第n位。。
只有第n位固定好后,才会回溯到决策前的状态,也就是固定第n-1位。
(上面有点绕,简单点说回溯法是会向下递归到最后才会开始向上回溯)
刚刚我们讨论了字符串不重复的情况,而在字符串有重复字符的时候,我们就得进行剪枝操作
例如字符串 “abb”
如果不进行剪枝,那么结果就会出现重复的 abb,bba,bab.
我们可以用hash表的contians()方法进行判断当前固定的字符之前是否已经固定过了。
递归解析:
- 结束条件:x=c.length-1时,当前可以选择的字符只有一个,所以直接将当前数组c加入到res。
- 递推参数:当前需要固定位x
- 递推工作: 初始化一个 Set ,用于排除重复的字符;将第 x 位字符与 i ∈ [x, len(c) ] 字符分别交换,并进入下层递归;
- 剪枝: 若 c[i] 在 Set 中,代表其是重复字符,因此 “剪枝” ;将 c[i] 加入 Set ,以便之后遇到重复字符时剪枝;
- 固定字符: 将字符 c[i] 和 c[x] 交换,即固定 c[i] 为当前位字符;
- 开启下层递归: 调用 dfs(x + 1) ,即开始固定第 x + 1 个字符;
- 还原交换: 将字符 c[i] 和 c[x] 交换(还原之前的交换);
二、代码
class Solution {
List<String> res = new LinkedList<>();
char[] c;
public String[] permutation(String s) {
c = s.toCharArray();
dfs(0);
return res.toArray(new String[res.size()]);
}
public void dfs(int x){
if(x==c.length-1){
res.add(String.valueOf(c));
}
HashSet<Character> set = new HashSet<>();
for(int i=x;i<c.length;i++){
if(set.contains(c[i])) continue;
set.add(c[i]);
swap(x,i);
dfs(x+1);
swap(x,i);
}
}
public void swap(int a,int b){
char temp =c[a];
c[a]=c[b];
c[b]=temp;
}
}
回溯算法详解参考
https://mp.weixin.qq.com/s/nMUHqvwzG2LmWA9jMIHwQQ
解析参考力扣用户Krahets
https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof/solution/mian-shi-ti-38-zi-fu-chuan-de-pai-lie-hui-su-fa-by/
侵删