JZ38 字符串的排列

【题目描述】
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

2023.1.28更新方法

回溯方法+set去重

public ArrayList<String> Permutation(String str) {
         Set<String> list = new HashSet<>();
         boolean[] visited = new boolean[str.length()];
         recursion(str,"",visited,list);
         ArrayList<String> res = new ArrayList<>(list);
         return res;
    }
    public void recursion(String str,String current,boolean[] visited,Set<String> res){
        if(str.length()==current.length()){
            res.add(current);
            return;
        }    
        for(int i = 0;i<str.length();i++)
        {
          if(!visited[i]){
              current+=str.charAt(i);
              visited[i] = true;
              recursion(str,current,visited,res);
              current = current.substring(0,current.length()-1);
              visited[i] = false;
            }
        } 
    }

【解题思路】

把字符串分为两部分:一部分是字符串的第一个字符,另一部分是第一个字符以后的所有字符。

  • 第一步是求所有可能出现在第一个位置的字符,即把第一个字符和后面所有字符交换。(for循环、交换操作)
  • 第二步是固定住第一个字符,求后面所有字符的排列。(递归)如何求剩余字符的排列:将剩余字符的第一个字符依次和后面的字符进行交换,
  • 而“求后面所有字符的排列”即可按照上面的思路递归进行。
  • 递归出口:求剩余字符的排列时只剩下一个字符

实现借助一个char[],通过交换元素得到不同的排列,在递归返回时将其装入ArrayList。

如下图所示,有两点需要注意:

  1. 在每个分支进行完后,要将交换过的元素复位,从而不会影响其他分支。
  2. 因为有“Swap A with A”这样的操作,并且题目指出可能有字符重复,所以分支末尾可能有重复的序列,在加入ArrayList要进行去重判断,不重复再加入。

    以 abc为例,图解过程如下:

代码如下:

public static ArrayList<String> Permutation(String str) {
    ArrayList<String> list = new ArrayList<>();
    if(str == null)
        return list;
    PermutationHelper(str.toCharArray(), list, 0);
    Collections.sort(list);//对所得的list集合进行排序
    return list;
}

public static void PermutationHelper(char[] ch, ArrayList res, int i){
    if(i == ch.length){
       String str = String.valueOf(ch);
        if(!res.contains(str)){
            res.add(str);
        }
    
    }
    for(int j = i; j < ch.length;j++){
        swap(ch,i,j);//交换数组第i个字符和第j个字符
        PermutationHelper(ch,res,i+1);
        swap(ch,i,j);//再次交换数组第i个字符和第j个字符,保证回到此次for循环前字符数组的状态,不影响字符数组进行下一次for循环
    }
}

public static void swap(char[] ch, int i ,int j) {
    char tmp = ch[i];
    ch[i] = ch[j];
    ch[j] = tmp;    

}


 
  1. 基于回溯思想的递归算法:

  2. * 1. 找出所有可能出现在字符串第一个位置的字符所得的字符串

  3. * 2. 对于步骤1中可能出现的每个字符串:固定第一个字符,求剩余字符的排列;求剩余字符的排列时跟原问题一样

  4. *

  5. * 如何求剩余字符的排列:将剩余字符的第一个字符依次和后面的字符进行交换

  6. * 递归出口:求剩余字符的排列时只剩下一个字符

  7. *

  8. * 以 a|b|c 为例,此时list为空,由于字符串不为空且长度不为0,进入到PermutationHelper()函数中

  9. *

  10. * 此时 i=0

  11. * j从0开始到2:

  12. *

  13. * j等于0 时:

  14. * 交换chars[0]和chars[0]后数组变成 a|b|c

  15. * 进入到PermutationHelper(chars,1,list)函数中:

  16. * 此时 i=1

  17. * j从1开始到2:

  18. * j等于1 时:

  19. * 交换chars[1]和chars[1]后数组变成 a|b|c

  20. * 进入到PermutationHelper(chars,2,list)函数中:

  21. * 此时 i=2

  22. * 由于此时i=2=数组长度-1,且list中不包含字符串abc,故将“abc”存入list中并返回,此时list={"abc"}

  23. * 交换chars[1]和chars[1]后数组变成 a|b|c

  24. * j等于2 时:

  25. * 交换chars[1]和chars[2]后数组变成 a|c|b

  26. * 进入到PermutationHelper(chars,2,list)函数中:

  27. * 此时 i=2

  28. * 由于此时i=2=数组长度-1,且list中不包含字符串cba,故将“acb”存入list中并返回,此时list={"abc","acb"}

  29. * 交换chars[1]和chars[2]后数组变成 a|b|c

  30. * 交换chars[0]和char[0]后数组变成 a|b|c

  31. *

  32. * j等于1 时:

  33. * 交换chars[0]和chars[1]后数组变成 b|a|c

  34. * 进入到PermutationHelper(chars,1,list)函数中:

  35. * 此时 i=1

  36. * j从1开始到2:

  37. * j等于1 时:

  38. * 交换chars[1]和char[1]后数组变成 b|a|c

  39. * 进入到PermutationHelper(chars,2,list)函数中:

  40. * 此时 i=2

  41. * 由于此时i=2=数组长度-1,且list中不包含字符串bac,故将“bac”存入list中并返回,此时list={"abc","acb","bac"}

  42. * 交换chars[1]和chars[1]后数组变成 b|a|c

  43. * j等于2 时:

  44. * 交换chars[1]和chars[2]后数组变成 b|c|a

  45. * 进入到PermutationHelper(chars,2,list)函数中:

  46. * 此时 i=2

  47. * 由于此时i=2=数组长度-1,且list中不包含字符串bca,故将“bca”存入list中并返回,此时list={"abc","acb","bac","bca"}

  48. * 交换chars[1]和chars[2]后数组变成 b|a|c

  49. * 交换chars[0]和chars[1]后数组变成 a|b|c

  50. *

  51. * j等于2 时:

  52. * 交换chars[0]和chars[2]后数组变成 c|b|a

  53. * 进入到PermutationHelper(chars,1,list)函数中:

  54. * 此时 i=1

  55. * j从1开始到2:

  56. * j等于1 时:

  57. * 交换chars[1]和char[1]后数组变成 c|b|a

  58. * 进入到PermutationHelper(chars,2,list)函数中:

  59. * 此时 i=2

  60. * 由于此时i=2=数组长度-1,且list中不包含字符串cba,故将“cba”存入list中并返回,此时list={"abc","acb","bac","bca","cba"}

  61. * 交换chars[1]和chars[1]后数组变成 c|b|a

  62. * j等于2 时:

  63. * 交换chars[1]和chars[2]后数组变成 c|a|b

  64. * 进入到PermutationHelper(chars,2,list)函数中:

  65. * 此时 i=2

  66. * 由于此时i=2=数组长度-1,且list中不包含字符串cab,故将“cab”存入list中并返回,此时list={"abc","acb","bac","bca","cba","cab"}

  67. * 交换chars[1]和chars[2]后数组变成 c|b|a

  68. * 交换chars[0]和chars[2]后数组变成 a|b|c

  69. *

  70. * 到此,for循环结束,此时list={"abc","acb","bac","bca","cba","cab"},PermutationHelper()函数调用结束

  71. * 利用Collections.sort()函数对list集合进行升序排序后得list={"abc","acb","bac","bca","cab","cba"}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值