字符串的排列

字符串的排列

题目描述

牛客地址:

https://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7

题目描述

题解

思路:
题意描述很简单,就是将一个字符数组的所有排列组合都列举出来即可.我们可以利用分治的思想,将大问题逐级分解成小问题,最终再汇总.
我们可以将思路整理成下面三点:
1.我们可以先固定第一个字符,求剩下n-1个字符的排列情况.求剩下n-1字符的排列情况,就变成了一个子问题,最终我们可以分解成求1个字符的排列情况.这里我们可以利用栈的思路逐级向下递归.
2.第一个字符其实不是固定的,它可能会换成别的字符,因此这里需要回溯,将第一个字符改成别的字符,然后又继续求剩下n-1个字符的排列情况.
3.如果有多个相同的字符,排列可能出现重复排列的情况,因此我们可以先将数组进行排序,将相同的字符放在一起,这样如果下一个遇到的字符和上一个相同,那么我们就可以直接跳过去.因为它们两个交换不会产生新的排列.
将思路用图解表示出来如下:
在这里插入图片描述

代码

import java.util.ArrayList;

/**
本题主要目标就是实现一个字符串数组的全排列,如何实现一个字符串数组的全部排列的情况?答案是递归+回溯.
我们可以采用分治的思想,利用递归将问题的规模逐渐缩小到一个字符的情况,这时候其实就是从头到尾的一次遍历,
也是一次全排列.然后就开始退栈,也就是从尾部最后一个字符开始重新进行排列组合,直到把所有的排列情况都列举出来即可.
 */
import java.util.Arrays;
public class Solution {
    public ArrayList<String> res = new ArrayList<String>();
    public boolean[] isVisited;
    public ArrayList<String> Permutation(String str) {
        if(str ==null || str.length() == 0) return res;
        //转化成char数组,因为需要排序
       char[] strArr = str.toCharArray();
       //排序的原因是将相同的字符放在一起,方面后面进行去重
       Arrays.sort(strArr);
       //设置一个是否访问过的标志位数组,方便后面回溯的时候判断
       isVisited = new boolean[str.length()];
       StringBuffer temp = new StringBuffer();
       //开始进行递归
       recursion(strArr,temp);
       return res;
    }

    public void recursion(char[] strArr,StringBuffer temp){
        if(strArr.length == temp.length()){
            res.add(new String(temp));
            return;
        }
        for(int i=0;i<strArr.length;i++){
            //如果被访问过了,则跳过
            if(isVisited[i]) continue;
            //这一点很容易被忽略掉,如果前一个和自己相同,且前一个没被访问过,则直接跳过
            //因为即使加进temp,也是重复的.
            //比如baa,若此时已经递归到最后一个a,这时候会回溯a(即temp删除a,a设置为未访问)
            //,接着再回溯a(temp又删除a,中间a设置为未访问),这时候for循环会在中间a的位置,
            //当for循环遍历下一个位置a,将最后一个a先加入temp时,且再进入递归的时候,
            //会再次想要将中间的a加入temp,这时候这个判断就起作用了
            if(i>0 && strArr[i] == strArr[i-1] && isVisited[i-1] == false) continue;
            //符合条件的加入到temp中去
            temp.append(strArr[i]);
            //标记已经访问过了
            isVisited[i] = true;
            //开始进行递归,分治
            recursion(strArr,temp);
            //递归回来的时候,要进行回溯
            isVisited[i] = false;
            temp.deleteCharAt(temp.length()-1);
        }
    }
}

复杂度分析:
时间复杂度:O(n∗n!),全排列的全部情况为n!,每次递归过程都是遍历字符串查找元素,这里是O(n)
空间复杂度:O(n),递归栈的最大深度为字符串长度n,临时字符串temp的空间也为O(n),res属于返回必要空间

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值