剑指offer第二版-38.字符串的排列

本系列导航:剑指offer(第二版)java实现导航帖](http://www.jianshu.com/p/010410a4d419)

面试题38:字符串的排列

题目要求:
输入一个字符串,打印出该字符串中字符的所有排列。如输入abc,则打印abc,acb,bac,bca,cab,cba。

解题思路:
排列组合是数学上的常见问题。解题思路与数学上求排列总数类似:首先确定第一个位置的元素,然后一次确定每一个位置,每个位置确实时把所有情况罗列完全即可。以abc为例,我之前更习惯于设置三个空,然后选择abc中的元素放入上述的空中。而书中给的思路是通过交换得到各种可能的排列,具体思路如下:

对于a,b,c(下标为0,1,2)
0与0交换,得a,b,c => 1与1交换,得a,b,c =>2与2交换,得a,b,c(存入)
                => 1与2交换,得a,c,b =>2与2交换,得a,c.b(存入)
0与1交换,得b,a,c => 1与1交换,得b,a,c =>2与2交换,得b,a,c(存入)
                => 1与2交换,得b,c,a =>2与2交换,得b,c,a(存入)
0与2交换,得c,b,a => 1与1交换,得c,b,a =>2与2交换,得c,b,a(存入)
                => 1与2交换,得c,a,b =>2与2交换,得c,a.b(存入)

书中解法并未考虑有字符重复的问题。对于有重复字符的情况如a,a,b,交换0,1号元素前后是没有变化的,即从生成的序列结果上看,是同一种排列,因此对于重复字符,不进行交换即可,思路如下:

对于a,a,b(下标为0,1,2)
0与0交换,得a,a,b => 1与1交换,得a,a,b =>2与2交换,得a,a,b(存入)
                => 1与2交换,得a,b,a =>2与2交换,得a,b,a(存入)
0与1相同,跳过
0与2交换,得b,a,a =>1与1交换,得b,a,a =>2与2交换,得b,a,a(存入)
                =>1与2相同,跳过

考虑了字符重复的解法的实现如下

package chapter4;

import java.util.*;

/**
 * Created by ryder on 2017/7/22.
 * 字符串的排列
 */
public class P197_StringPermutation {
    public static List<char[]> permutation(char[] strs) {
        if (strs == null || strs.length == 0)
            return null;
        List<char[]> ret = new LinkedList<>();
        permutationCore(strs, ret, 0);
        return ret;
    }
    //下标为bound的字符依次与[bound,length)的字符交换,如果相同不交换,直到最后一个元素为止。
    //如a,b,c
    //0与0交换,得a,b,c => 1与1交换,得a,b,c =>2与2交换,得a,b,c(存入)
    //                => 1与2交换,得a,c,b =>2与2交换,得a,c.b(存入)
    //0与1交换,得b,a,c => 1与1交换,得b,a,c =>2与2交换,得b,a,c(存入)
    //                => 1与2交换,得b,c,a =>2与2交换,得b,c,a(存入)
    //0与2交换,得c,b,a => 1与1交换,得c,b,a =>2与2交换,得c,b,a(存入)
    //                => 1与2交换,得c,a,b =>2与2交换,得c,a.b(存入)

    //如a,a,b
    //0与0交换,得a,a,b => 1与1交换,得a,a,b =>2与2交换,得a,a,b(存入)
    //                => 1与2交换,得a,b,a =>2与2交换,得a,b,a(存入)
    //0与1相同,跳过
    //0与2交换,得b,a,a =>2与2交换,得b,a,a(存入)
    public static void permutationCore(char[] strs, List<char[]> ret, int bound) {
        if (bound == strs.length)
            ret.add(Arrays.copyOf(strs, strs.length));
        Set<Character> set = new HashSet<>();
        for (int i = bound; i < strs.length; i++) {
            if (set.add(strs[i])) {
                swap(strs, bound, i);
                permutationCore(strs, ret, bound + 1);
                swap(strs, bound, i);
            }
        }
    }

    public static void swap(char[] strs, int x, int y) {
        char temp = strs[x];
        strs[x] = strs[y];
        strs[y] = temp;
    }

    public static void main(String[] args) {
        char[] strs = {'a', 'b', 'c'};
        List<char[]> ret = permutation(strs);
        for (char[] item : ret) {
            for (int i = 0; i < item.length; i++)
                System.out.print(item[i]);
            System.out.println();
        }
        System.out.println();
        char[] strs2 = {'a', 'a', 'b','b'};
        List<char[]> ret2 = permutation(strs2);
        for (char[] item : ret2) {
            for (int i = 0; i < item.length; i++)
                System.out.print(item[i]);
            System.out.println();
        }
    }
}

运行结果

abc
acb
bac
bca
cba
cab

aabb
abab
abba
baab
baba
bbaa

推荐阅读更多精彩内容

  • 字符串的全排列 题目描述: 输入一个字符串,打印出该字符串中字符的所有排列。 例如输入字符串abc,则输出由字符a...
    MinoyJet阅读 5,288评论 4 10
  • 总结 想清楚再编码 分析方法:举例子、画图 第1节:画图分析方法 对于二叉树、二维数组、链表等问题,都可以采用画图...
    M_巴拉巴拉阅读 912评论 0 7
  • 剑指Offer笔试题(2) 题目来源:牛客网 题目一 复杂链表的复制 描述: 输入一个复杂链表(每个节点中有节点...
    Torang阅读 1,116评论 2 4
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 25,875评论 18 394
  • 本系列导航:剑指offer(第二版)java实现导航帖 面试题:字符串的组合 题目要求:输入一个字符串,打印出该字...
    ryderchan阅读 469评论 0 2