字符串的排列Java

刷题记录第22题,上一题:数据流中的中位数,本题地址:字符串的排列

题目描述:
输入一个字符串,打印出该字符串中字符的所有排列。

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例:

输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]

限制:
1 <= s 的长度 <= 8

这道题是一道典型的回溯法问题。在之前的八皇后问题的博客中也采用了回溯的思想。

在算法小抄的一书中说到:解决一个回溯问题,实际上就是一个决策树的遍历过程,且这个算法做的过程很基础,就是穷举过程。

  • 路径:也就是已经做出的选择;
  • 选择列表:也就是当前可做出的选择;
  • 结束条件:也就是到达决策树底部,无法再做出选择的条件;

回溯算法的代码框架如下:

private void backtrack(路径, 选择列表){
    if 满足结束条件{
        result.add(路径)
        return
    }
    
    for 选择 in  选择列表{
        做选择
        backtrack(路径, 选择列表)
        撤销选择
    }
}

其核心也就在于,再for循环的递归,在递归调用前做出选择,在递归调用结束后撤销选择

本题中输入的字符串,没有明确说明是否是每个字母只出现一次,所以这里的字母其实可以出现多次。也就是比如aaa这样的字符串。

解决该问题的一种较为直观的思路是,我们首先生成所有的排列,然后进行去重。而另一种思路是我们通过修改递归函数,使得该递归函数只会生成不重复的序列。

class Solution {
    private List<String> list;
    boolean[] vis;
    public String[] permutation(String s) {
        list = new ArrayList<>();
        vis = new boolean[s.length()];
        backtrack(s.toCharArray(), 0, s.length(), new StringBuffer());

        // 去重
        HashSet<String> strings = new HashSet<>(list); // List<String> list;
        String[] res = new String[strings.size()];
        int idx = 0;
        for (String string : strings) {
            res[idx++] = string;
        }
        return res;
    }
	
	// i 表示当前位置,n表示字符串长度
    private void backtrack(char[] arr, int i, int length, StringBuffer stringBuffer) {
        if(i == length && stringBuffer.length() > 0){
            list.add(stringBuffer.toString());
            return;
        }
        for (int j = 0; j < length; j++) {
            if(vis[j])
                continue;
            vis[j] = true;
            stringBuffer.append(arr[j]);
            backtrack(arr, i + 1, length, stringBuffer);// 探测下一个位置
            stringBuffer.deleteCharAt(stringBuffer.length() - 1);
            vis[j] = false;
        }
    }
}

另一种,在递归函数中去重的思路为:在递归函数中设定一个规则,保证在填每一个空位的时候重复字符只会被填入一次。具体地,我们首先对原字符串排序,保证相同的字符都相邻,在递归函数中,我们限制每次填入的字符一定是这个字符所在重复字符集合中「从左往右第一个未被填入的字符」,即如下的判断条件:

if (vis[j] || (j > 0 && !vis[j - 1] && s[j - 1] == s[j])) {
    continue;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦否

文章对你有用?不妨打赏一毛两毛

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值