剑指27.字符串的排列

题目描述

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

输入描述:

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

思路

求整个字符串的排列,可以看成两步。

第一步:求所有可能出现在第一个位置的字符,即把第一个字符先与自身交换,再和后面所有的字符交换。每交换完一次相当于确定了一个字符。

第二步:固定第一个字符,求后面所有字符的排列。   例如接着确定下一个字符即第二个字符,让它再与自身交换然后与它之后的字符逐一交换……典型的递归

需要注意的地方:“输出按字典序排”—因此需要有排序操作;“可能由字符重复”—因此需要去重

解法1

import java.util.*;
public class Solution {
    /*
     *  通过递归去查找每一个位置的字符可能出现的情况。
     *  比如要找index位置的字符,那么交换index以及index位置之后的那些字符即可。
     */
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> res = new ArrayList<>();
        if (str.length() == 0 || str == null)
            return res;
        char[] strArray = str.toCharArray();  // 把字符串String转化为char类型的字符数组
        solve(strArray,0,res);
        res = new ArrayList<String>(new HashSet<String>(res)); // 去重操作
        Collections.sort(res);  // 字典排序 -> 等价于 res.sort(null);  null意思是升序
        return res;
    }
    private void solve(char[] strArray, int index, ArrayList<String> res) {
        int length = strArray.length;
        if (index == length - 1){ // 把当前确定的结果(char数组的形式)转化为字符串保存下来
            res.add(String.valueOf(strArray)); //  String.valueOf(strArray) 把字符数组转化为字符串
        }else{
            // 确定index位置的字符
            // i要从index位置开始,原因是当前index位置的字符可以是原始字符串所对应的字符
            for (int i = index; i < length; i++) {
                swap(strArray,index,i);  // 交换index位置和index位置之后的字符
                // 当前index位置的字符已经通过交换找到了,接着递归找下一个位置的字符
                solve(strArray, index + 1, res);
                // 消除当前层递归时交换字符的影响,不消除会造成原index位置的字符发生变化
                // 即字符串输出后要将字符交换回来,变回原始的字符串,以便进行下一轮交换。
                swap(strArray,index,i);
            }
        }
    }
    private void swap(char[] strArray, int a, int b){
        char temp = strArray[a];
        strArray[a] = strArray[b];
        strArray[b] = temp;
    }
}

解法2(在解法1的基础上,使用TreeSet)

import java.util.*;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> res = new ArrayList<>();
        if (str.length() == 0 || str == null)
            return res;
        TreeSet<String> treelist = new TreeSet<>(); //TreeSet是一个有序的集合,它的作用是提供有序的Set集合。
        Solve(str.toCharArray(),0,treelist);
        for (String s : treelist)
            res.add(s);
        return res;
    }
    private void Solve(char[] strArray, int index, TreeSet<String> treelist) {
        int length = strArray.length;
        if (index == length - 1){
            treelist.add(String.valueOf(strArray));
        }else{
            for (int i = index; i < length; i++) {
                swap(strArray,index,i);
                Solve(strArray,index + 1,treelist);
                swap(strArray,index,i);
            }
        }
    }
    private void swap(char[] strArray, int a, int b) {
        char temp = strArray[a];
        strArray[a] = strArray[b];
        strArray[b] = temp;
    }
}

拓展(字符串的全组合)

首先对比一下全排列和全组合,给定一个字符串"abc",

  • 全组合形式:a,b,c,ab,ac,bc,abc
  • 全排列形式:abc,acb,bac,bca,cab,cba

 交换字符串中的两个字符时,虽然能得到两个不同的排列,但却是同一个组合。比如ab和ba是不同的排列,但只算一个组合。

 全组合思路:

 

Java实现对 数字 进行全组合

import java.util.ArrayList;
public class test {
    public static void main(String[] args) {
        int[] A = {1,2,3,4};
        for (int i = 1; i <= A.length ; i++) {
            ArrayList<Integer> res = new ArrayList<>();
            getCombination(A, i, 0, res);
        }
    }
    private static void getCombination(int[] A, int m, int start, ArrayList<Integer> res) {
        if (m == 0){
            // m个元素已经找齐,则打印
            for (Integer i : res){
                System.out.print(i + " ");
            }
            System.out.println();
            return;
        }
        if (start < A.length){
            res.add(A[start]);
            //使用A[start],从剩下的(n-start)-1个元素中找m - 1个元素求它的组合
            getCombination(A,m - 1, start + 1, res);
            //不使用A[start],从剩下(n-start)-1个元素中找m个元字符,求它的组合
            res.remove(res.size() - 1);//删掉本次添加的元素
            getCombination(A,m, start + 1, res);
        }
    }
}
View Code

Java实现对 字符 进行全组合

import java.util.ArrayList;
public class test {
    public static void main(String [] args) {
        String str = "abc";
        ArrayList<String> res = new ArrayList<>();
        for (int i = 1; i <= str.length(); i++) {
            multiCombination(str.toCharArray(), i,0, res);
        }
    }
    private static void multiCombination(char[] strArray, int m, int start,ArrayList<String> res) {
        if (m == 0) {
            System.out.println(res);
            return;
        }
        if (start < strArray.length) {
            res.add(String.valueOf(strArray[start]));
            multiCombination(strArray, m - 1,start + 1, res); // 从剩余的n-1个中选择m-1个
            res.remove(res.size() - 1);
            multiCombination(strArray, m,start + 1, res);  // 从剩余的n-1个中选择m个
        }
    }
}
View Code

举一反三

如果题目是按照一定要求摆放若干个数字,则可以先求出这些数字的所有排列,然后一一判断每个排列是不是满足题意。

总结

去重操作:可以用HashMap()去重,也可以使用 list.contains() 方法判断是否有重复字符串,如果没有重复,再进行 list.add操作。

排序操作:Collections.sort(list)可以将list中的字符串进行排序。

字符串和字符数组的相互转化:str.toCharArray()     String.valueOf(strArray)

字符在递归过程中进行了交换后,要记得交换回来。

参考:

【Java】 剑指offer(38) 字符串的排列

原文地址:https://www.cnblogs.com/HuangYJ/p/13510648.html