剑指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); } } }
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个 } } }
举一反三
如果题目是按照一定要求摆放若干个数字,则可以先求出这些数字的所有排列,然后一一判断每个排列是不是满足题意。
总结
去重操作:可以用HashMap()去重,也可以使用 list.contains() 方法判断是否有重复字符串,如果没有重复,再进行 list.add操作。
排序操作:Collections.sort(list)可以将list中的字符串进行排序。
字符串和字符数组的相互转化:str.toCharArray() String.valueOf(strArray)
字符在递归过程中进行了交换后,要记得交换回来。
参考: