學習的博客
1. 字符串排列
給定一個字符串,打印出該字符串中字符的所有排列,這裏有個注意點就是字符串中是否有重複字符,例如abc,它的全排列有abc, acb, bac, bca, cab, cba; 但是對於字符串abb,它的全排列只有abb,bab,bba。
對於沒有重複字符的字符串
- 首先,我們固定第一個字符a,求後面兩個字符bc的排列。
- 當兩個字符bc排列求好之後,我們把第一個字符a和後面的b交換,得到bac,接着我們固定第一個字符b,求後面兩個字符ac的排列。
- 現在是把c放在第一個位置的時候了,但是記住前面我們已經把原先的第一個字符a和後面的b做了交換,爲了保證這次c仍是和原先處在第一個位置的a交換,我們在拿c和第一個字符交換之前,先要把b和a交換回來。在交換b和a之後,再拿c和處於第一位置的a進行交換,得到cba。我們再次固定第一個字符c,求後面兩個字符b、a的排列。
對於有重複字符的字符串
由於全排列就是從第一個數字起,每個數分別與它後面的數字交換,我們先嚐試加個這樣的判斷——如果一個數與後面的數字相同那麼這兩個數就不交換了。例如abb,第一個數與後面兩個數交換得bab,bba。然後abb中第二個數和第三個數相同,就不用交換了。但是對bab,第二個數和第三個數不同,則需要交換,得到bba。由於這裏的bba和開始第一個數與第三個數交換的結果相同了,因此這個方法不行。
換種思維,對abb,第一個數a與第二個數b交換得到bab,然後考慮第一個數與第三個數交換,此時由於第三個數等於第二個數,所以第一個數就不再用與第三個數交換了。再考慮bab,它的第二個數與第三個數交換可以解決bba。此時全排列生成完畢!
這樣,我們得到在全排列中去掉重複的規則:
去重的全排列就是從第一個數字起,每個數分別與它後面非重複出現的數字交換。
以下是這兩種情況下的java實現
package sxd.learn.java.questions;
public class StringPermutation {
private int counts;
public static void main(String[] args) {
new StringPermutation().run();
}
public void run() {
StringBuilder str = new StringBuilder("abb");
permutation(str);
permutation2(str);
}
/**
* 字符串全排列,字符串中沒有重複字符
*/
public void permutation(StringBuilder str) {
if (str == null)
return;
permutation(str, 0);
}
private void permutation(StringBuilder str, int begin) {
if (begin == str.length() - 1) {
System.out.println(++counts + ":" + str);
} else {
for (int i = begin; i < str.length(); i++) {
exchange(str, begin, i);
permutation(str, begin + 1);
exchange(str, begin, i);
}
}
}
/**
* 字符串全排列,字符串中有重複字符
* 去重的全排列是從第一個數字起每個數分別與它後面非重複出現的數字交換
*/
public void permutation2(StringBuilder str){
if(str != null){
permutation2(str, 0);
}
}
private void permutation2(StringBuilder str, int begin) {
if (begin == str.length() -1) {
System.out.println(counts++ + ":" + str);
} else {
for (int i = begin; i < str.length(); i++) {
if (isExchange(str, begin, i)) {
exchange(str, begin, i);
permutation2(str, begin + 1);
exchange(str, begin, i);
}
}
}
}
private boolean isExchange(StringBuilder str, int begin, int end) {
for (int i = begin; i < end; i++) {
if (str.charAt(i) == str.charAt(end))
return false;
}
return true;
}
private void exchange(StringBuilder str, int i, int j) {
char temp = str.charAt(i);
str.setCharAt(i, str.charAt(j));
str.setCharAt(j, temp);
}
}
- 字符串組合
給定一個字符串,求該字符串中字符的所有組合。例如,如果輸入abc,它的組合有a、b、c、ab、ac、bc、abc。
假設我們想在長度爲n的字符串中求m個字符的組合。我們先從頭掃描字符串的第一個字符。針對第一個字符,我們有兩種選擇:第一是把這個字符放到組合中去,接下來我們需要在剩下的n-1個字符中選取m-1個字符;第二是不把這個字符放到組合中去,接下來我們需要在剩下的n-1個字符中選擇m個字符。這兩種選擇都很容易用遞歸實現。
package sxd.learn.java.questions;
import java.util.Vector;
public class StringCombination {
private int counts;
public static void main(String[] args) {
new StringCombination().run();
}
public void run(){
combination(new StringBuilder("abc"));
}
public void combination(StringBuilder str){
if(str == null)
return;
Vector<Character> vector = new Vector<>();
for (int i = 1; i <= str.length(); i++) {
combination(str, i, 0, vector);
}
}
private void combination(StringBuilder str, int number, int start, Vector<Character> vector){
if(number == 0){
System.out.print(++counts + ":");
for (Character character : vector) {
System.out.print(character);
}
System.out.println();
return;
}
if(start == str.length())
return;
vector.add(str.charAt(start));
combination(str, number - 1, start + 1, vector);
vector.remove(vector.size() - 1);
combination(str, number, start + 1, vector);
}
}