题目:
编写一个方法,确定某字符串的所有排列组合。
解法一:
代码:
1 import java.util.ArrayList; 2 3 public class 全排列1 { 4 5 public static void main(String[] args) { 6 ArrayList<String> res = getPermutation0("abcd",4-1); 7 System.out.println(res.size()); 8 System.out.println(res); 9 System.out.println("====================================="); 10 res = getPermutation1("abcd"); 11 System.out.println(res.size()); 12 System.out.println(res); 13 } 14 15 // 迭代法的递归形式 16 public static ArrayList<String> getPermutation0(String A,int n){ 17 if (n==0) { 18 ArrayList<String> res = new ArrayList<>(); 19 res.add(A.charAt(n)+""); 20 return res; 21 } 22 ArrayList<String> res = getPermutation0(A, n-1); 23 char c = A.charAt(n); 24 ArrayList<String> res_new = new ArrayList<>(); 25 for (String str : res) {// 访问上一趟集合中的每个字符串 26 // 插入到每个位置,形成一个新串 27 String newStr = c + str;// 加在前面 28 res_new.add(newStr); 29 newStr = str + c;// 加在后面 30 res_new.add(newStr); 31 // 加在中间 32 for (int j = 1; j < str.length(); j++) { 33 newStr = str.substring(0, j) + c + str.substring(j); 34 res_new.add(newStr); 35 } 36 } 37 return res_new; 38 } 39 40 /*逐步生成大法-迭代法*/ 41 public static ArrayList<String> getPermutation1(String A) { 42 int n = A.length(); 43 ArrayList<String> res = new ArrayList<>(); 44 res.add(A.charAt(0) + "");// 初始化,包含第一个字符 45 46 for (int i = 1; i < n; i++) {// 第二个字符插入到前面生成集合的每个元素里面 47 ArrayList<String> res_new = new ArrayList<>(); 48 char c = A.charAt(i);// 新字符 49 for (String str : res) {// 访问上一趟集合中的每个字符串 50 // 插入到每个位置,形成一个新串 51 String newStr = c + str;// 加在前面 52 res_new.add(newStr); 53 newStr = str + c;// 加在后面 54 res_new.add(newStr); 55 // 加在中间 56 for (int j = 1; j < str.length(); j++) { 57 newStr = str.substring(0, j) + c + str.substring(j); 58 res_new.add(newStr); 59 } 60 } 61 res = res_new;// 更新 62 63 } 64 return res; 65 } 66 67 }
结果:
解法二:交换法,大体意思就是不开辟新的辅助空间,直接在原有的数组中进行两个元素的交换即可,但是要注意这种方法必须要进行回溯。
代码:
1 import java.util.ArrayList; 2 import java.util.Arrays; 3 4 public class 全排列2 { 5 6 public static void main(String[] args) { 7 ArrayList<String> res = getPermutation("123"); 8 System.out.println(res.size()); 9 System.out.println(res); 10 } 11 12 static ArrayList<String> res = new ArrayList<>(); 13 14 public static ArrayList<String> getPermutation(String A) { 15 char[] arr = A.toCharArray(); 16 Arrays.sort(arr);// abc 17 getPermutationCore(arr, 0); 18 return res; 19 } 20 21 private static void getPermutationCore(char[] arr, int k) { 22 if (k == arr.length) {// 排好了一种情况,递归的支路走到底了 23 res.add(new String(arr)); 24 } 25 26 // 从k位开始的每个字符,都尝试放在新排列的k这个位置 27 for (int i = k; i < arr.length; i++) { 28 swap(arr, k, i);// 把后面每个字符换到k位 29 getPermutationCore(arr, k + 1); 30 swap(arr, k, i);// 回溯 31 } 32 } 33 34 // 交换位置 35 static void swap(char[] arr, int i, int j) { 36 char tmp = arr[i]; 37 arr[i] = arr[j]; 38 arr[j] = tmp; 39 } 40 41 }
结果:
例题:
LeetCode60 n个数的排列组合找出第k个排列。
思路:在上面的代码中我们虽然已经完成了n的元素的全排列,可是它的结果并不是按照字典序排列,那对于这道题目来说,按照上面的思路就很难完成了,当然我们也可以对上面的解法的答案再进行排序,这也算是一种方法。这里要介绍的是一种叫做前缀法的方法。思路就是每次从头顺序扫描源数组(当然要先把源数组排序),只要该元素不在前缀里面,那么就把这个元素附加到前缀的后面形成新的前缀,直到这个前缀的长度等于源串的长度就不再继续加下去了。然后继续这样循环下去就能得到按照字典序排列的全排列,在循环过程中依次计数即可,这样就能找出第k个排列了。
代码:
1 public class 全排列3 { 2 3 public static void main(String[] args) { 4 String s = "123"; 5 permutation("", s.toCharArray()); 6 } 7 8 final static int k = 3; 9 static int count = 0; 10 11 private static void permutation(String prefix, char[] arr) { 12 if (prefix.length() == arr.length) {// 前缀的长度==字符集的长度,一个排列就完成了 13 // System.out.println(prefix); 14 count++; 15 if (count == k) { 16 System.out.println("-------:" + prefix); 17 System.exit(0); 18 } 19 } 20 // 每次都从头扫描,只要该字符可用,我们就附加到前缀后面,前缀变长了 21 for (int i = 0; i < arr.length; i++) { 22 char ch = arr[i]; 23 // 这个字符可用:在pre中出现次数<在字符集中的出现次数 以防有元素重复的情况 24 if (count(prefix, ch) < count(arr, ch)) { 25 permutation(prefix + ch, arr); 26 } 27 } 28 } 29 30 private static int count(char[] arr, char ch) { 31 int cnt = 0; 32 for (char c : arr) { 33 if (c == ch) 34 cnt++; 35 } 36 return cnt; 37 } 38 39 private static int count(String str, char ch) { 40 int cnt = 0; 41 for (int i = 0; i < str.length(); i++) { 42 if (str.charAt(i) == ch) 43 cnt++; 44 } 45 return cnt; 46 } 47 48 }
结果: