“Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。”
一、题目描述:
输入一个字符串,打印出该字符串中字符的所有排列。你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = "abc" 输出:["abc","acb","bac","bca","cab","cba"] 限制:
1 <= s 的长度 <= 8
二、思路分析:
1. 回溯
使用via记录是否遍历过,当储存的数据长度等于输入字符串s长度就储存在输出的ans数组。
回溯要点就是 递归回到本层时要取消之前的操作
但这样当有重复字符时,会出现重复的排序。
- 我们可以在得出所有排序后用set等方法去重,
- 也可以现将输入s转换为数组后排序,这样使得重复字符集中。在递归函数中,我们限制每次填入的字符一定是这个字符所在重复字符集合中「从左往右第一个未被填入的字符」。
if (via[j] || (j > 0 && !via[j - 1] && arr[j - 1] === arr[j])) {
continue;
}
2. 利用下一个更大排序思路
力扣题目下一个排序
我们先把数组按升序排列得到最小的排序组合,然后通过这个排序一步一步得到比之前更大的下一个组合,通过该算法也能解决重复问题
为了让变大的幅度尽可能小,我们需要将一个左边的「较小数」与一个右边的「较大数」交换,以能够让当前排列变大,从而得到下一个排列。同时这个较小数要尽可能在右边,较大数尽可能在左边(但要在较小数右边 不然没有意义)。故我们都从末尾遍历。
而这样的较小数右边一定是降序排列的,与一个右边的「较大数」交换后也任然是降序排列,故我们再把较小数后面的翻转后变成升序排序,肯定比交换后的更小,这样我们得到了该排列的下一个更大排列
三、AC代码
- 回溯
/**
* @param {string} s
* @return {string[]}
*/
var permutation = function(s) {
const search=function(index){
if(index===s.length){
let s=ans.join("")
arr.push(s)
return
}
for(let i=0;i<s.length;i++){
if(via[i]||(i>0&&!via[i-1]&&num[i]===num[i-1]))continue;
ans.push(num[i]);
via[i]=true;
search(index+1);
via[i]=false;
ans.pop();
}
}
let num=Array.from(s).sort()
let ans=[],via=new Array(s.length).fill(false);
let arr=[];
search(0);
return arr
};
- 利用下一个更大排序思路
/**
* @param {string} s
* @return {string[]}
*/
var permutation = function(s) {
let nums=Array.from(s),ans=[]
const swap=(i,j)=>{
[nums[i],nums[j]]=[nums[j],nums[i]]
}
const reverse=(start)=>{
let end=nums.length-1;
while(start<end){
swap(start,end)
start++
end--
}
}
const nextBiger=function(){
let i=nums.length-2;
while(i>=0&&nums[i]>=nums[i+1])i--
if(i<0)return false;
let j=nums.length-1
while(j>i&&nums[j]<=nums[i])j--
swap(i,j)
reverse(i+1)
return true;
}
do{
ans.push(nums.join(""))
}while(nextBiger())
return ans
};