这是我参与更文挑战的第8天,活动详情查看:更文挑战
剑指 Offer 38. 字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例
输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]
提示
1 <= s 的长度 <= 8
个人思路解析
方式一:回溯
这类涉及到全排列的问题,一般都会想到使用回溯来实现,通过交换元素位置来获取不同排列的字符串
class Solution {
private Set<String> set;
public String[] permutation(String s) {
// 得到字符数组,方便元素交换位置
char[] c = s.toCharArray();
// 哈希集合去重
set = new HashSet<>();
// 回溯
back(c, 0, "");
// 返回结果
String[] ans = new String[set.size()];
return set.toArray(ans);
}
private void back(char[] arr, int index, String str){
// 边界,将排序后的字符串添加到集合中
if(index == arr.length){
set.add(str);
return;
}
// 遍历后续的元素,将每一位元素与 index 位置元素进行交换
for(int i = index; i < arr.length; ++i){
// 去重
if(i != index && arr[i] == arr[index]){
continue;
}
// 交换元素
exchange(arr, index, i);
back(arr, index + 1, str + arr[index]);
// 恢复
exchange(arr, index, i);
}
}
private void exchange(char[] arr, int l, int r){
char c = arr[l];
arr[l] = arr[r];
arr[r] = c;
}
}
提交结果
方式二:下一个排列
通过将字符数组进行一个从小到大的排列,再重复去获取下一个比当前数组大的排列,可以有效避免出现重复的情况
例如:1, 3, 2
- 从后向前,找到非递增的首位元素
i, 此时i = 0,当前元素为1 - 从后向前,找到首个比
c[i]大的元素2 - 交换两个元素的位置
2, 3, 1 - 同时将
i后面的元素进行反转2, 1, 3,这样即可得到最接近当前数组值的下一组数组
class Solution {
public String[] permutation(String s) {
List<String> list = new ArrayList<>();
char[] c = s.toCharArray();
// 排列数组
Arrays.sort(c);
do{
// 添加到结果集
list.add(new String(c));
}while(nextPermutation(c));// 判断是否还有下一个排列
// 返回结果
String[] res = new String[list.size()];
return list.toArray(res);
}
private boolean nextPermutation(char[] c){
// 从后向前,找到非递增的首位元素
int i = c.length - 2;
while(i >= 0 && c[i] >= c[i + 1]){
--i;
}
// 边界
if(i < 0){
return false;
}
// 从后向前,找到首个比 c[i] 大的元素
int j = c.length - 1;
while(j > i && c[i] >= c[j]){
--j;
}
// 交换两个元素的位置
swap(c, i, j);
// 同时将 i 后面的元素进行反转
reverse(c, i + 1);
return true;
}
// 交换元素
private void swap(char[] c, int i, int j){
char temp = c[i];
c[i] = c[j];
c[j] = temp;
}
// 数组反转
private void reverse(char[] c, int i){
int j = c.length - 1;
while(i < j){
swap(c, i, j);
++i;
--j;
}
}
}