星級:1
1.字符串的排列
【題目】
輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串 abc, 則打印出由字符 a,b,c 所能排列出來的所有字符串 abc,acb,bac,bca,cab 和 cba。
輸入一個字符串,長度不超過9(可能有字符重複),字符只包括大小寫字母。
【代碼】
package swear2offer.strs;
import java.util.ArrayList;
import java.util.TreeSet;
public class StrRange {
/**
* 輸入一個字符串,按字典序打印出該字符串中字符的所有排列。
* 例如輸入字符串 abc, 則打印出由字符 a,b,c
* 所能排列出來的所有字符串 abc,acb,bac,bca,cab 和 cba。
*
* 輸入一個字符串,長度不超過9(可能有字符重複),字符只包括大小寫字母。
* */
StringBuilder path = new StringBuilder();
// 使用HashSet是爲了保證存儲的stringBuilder不存在重複
TreeSet<String> paths = new TreeSet<>();
// 標記數組
boolean[] flag;
ArrayList<String> res = new ArrayList<>();
public ArrayList<String> Permutation(String str) {
if (str.equals("") || str == null) return res;
char[] arr = str.toCharArray();
flag = new boolean[arr.length];
All(arr,0);
res.addAll(paths);
return res;
}
/**
* 符合條件的時候返回,不符合條件的時候回溯
* */
public void All(char[] str, int len) {
// 回溯跳出條件
if (len == str.length) {
paths.add(path.toString());
return;
}
for (int i=0; i<str.length; i++) {
if (!flag[i]) {
flag[i] = true;
path.append(str[i]);
All(str,len+1);
flag[i] = false;
path.deleteCharAt(path.length()-1);
}
}
}
public static void main(String[] args) {
String s = "abc";
System.out.println(new StrRange().Permutation(s).toString());
}
}
【思考】
- 在沒有重複的情況下是全排列,有重複的情況下使用set去重,又因爲是需要字典序輸出,所以使用treeset保存結果。
- 全排列這種情況通常是需要使用回溯的,回溯的幾個重點:
- 算子for循環
- 單次路徑和路徑總個數
- 標記數組,用於回溯
- 回溯終止的條件
- 回溯的公式
2.把數組排成最小的數
【題目】
輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。例如輸入數組 {3,32,321},則打印出這三個數字能排成的最小數字爲 321323。
【代碼】
/**
* 輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,
* 打印能拼接出的所有數字中最小的一個。例如輸入數組 {3,32,321},
* 則打印出這三個數字能排成的最小數字爲 321323。
*
* 利用回溯的思想,
* 標記數組,總路徑,單路徑,算子循環,回溯
* */
TreeSet<String> paths;
StringBuilder path;
boolean[] flag;
public String PrintMinNumber(int [] numbers) {
int n;
n = numbers.length;
if (n == 0 || numbers == null) return "";
paths = new TreeSet<>();
path = new StringBuilder();
flag = new boolean[n];
Find(numbers,0);
return paths.first();
}
/**
* n相當於一個標記
* 用來判斷什麼時候達到回溯的終點
* */
public void Find(int[] a, int n) {
if (n == a.length) {
paths.add(path.toString());
return;
}
int i;
for (i=0; i<a.length; i++) {
if (!flag[i]) {
flag[i] = true;
path.append(a[i]);
Find(a,n+1);
flag[i] = false;
// 因爲加進去的是字符串,所以需要指定範圍刪除
int start,end;
String add = a[i]+"";
start = path.length() - add.length();
end = path.length();
path.delete(start, end);
}
}
}
【思考】
使用回溯的方法複雜度要搞點,本題還可以使用排序的方法,先把數組變成字符串,然後給這些字符串排序,之後再把字符串連接起來即可