題目:
編寫一個方法,確定某字符串的所有排列組合。
解法一:
代碼:
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 }
結果: