在面試中經常遇到字符串全排列問題,現整理常見情況如下:
1、輸入字符串,輸出該串中所有字符能夠排列出來的字符串,例輸入”abc“,輸出”abc、acb、bac、bca、cab 、cba“
不考慮字符串重複,該問題是求所有字符的排列,有A(n, n) = n! 種情況,其中n是字符串長度。
遞歸思路:先確定字符串(n)第一位上的字符str[0],字符串(n-1)爲字符串(n)第一位後面的子字符串,對字符串(n-1)進行全排列遞歸,終止條件是遞歸到字符串(1),即子字符串長度爲1。這樣就完成了第一位字符爲str[0]的情況,然後就需要更換第一位字符,即依次將str[0]與str[1]、str[2]、...、str[n-1]調換,完成所有情況。
代碼如下:
public class CodeFour{
public static void main(String[] args){
String str = "ab";
char[] arr = str.toCharArray();
fun1(arr, 0, str.length());
}
public static void fun1(char[] arr, int begin, int len){
if(len-begin<=1){
System.out.println(String.valueOf(arr));
return;
}else{
for(int i=begin;i<len;i++){
swap(arr,i,begin);
fun1(arr,begin+1,len);
swap(arr,i,begin);
}
}
}
public static void swap(char[] arr, int index1, int index2){
char temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
}
2、輸入字符串的字符互不相同,輸出與該字符串長度相同的字符串,要求字符都來自原字符串,每個字符可以重複,例輸入”ab“,輸出”aa、ab、ba、bb“
不考慮字符串重複,該問題是求所有字符的組合,有pow(n, n)種情況。
遞歸思路:先確定字符串(n)第一位上的字符str[0],字符串(n-1)爲字符串(n)第一位後面的子字符串,然後遞歸。與問題1不同的是,問題1中字符不能重複,因此每次遞歸需要交換字符位置,爲了保證字符串不變每次還需要交換回來。而問題2字符是可以重複,因此需要有一個數組用於存儲結果。
代碼如下:
public class CodeThree{
static int count = 0;
public static void main(String[] args){
String str = "abc";
char[] arr = str.toCharArray();
char[] rst = new char[str.length()];
fun2(arr, rst, 0, str.length());
}
public static void fun2(char[] arr, char[] rst, int begin, int len){
if(len-begin<1){
count++;
System.out.println(count+": "+String.valueOf(rst));
return;
}else{
for(int i=0;i<len;i++){
rst[begin] = arr[i];
fun2(arr,rst,begin+1,len);
}
}
}
}
面試題整理【3】中第四題也可以採用此題遞歸的思想實現,先找出g、9字符出現的位置然後在該位置上進行字符組合。代碼可見網頁http://taop.marchtea.com/01.06.html
3、輸入一個字符串,輸出該字符串中字符的所有組合。例,輸入abc,輸出a、b、c、ab、ac、bc、abc
思路1:與上文1、2方法類似,每次刪除一個字符,不過會產生很多重複的字符
思路2:即先求C(n, m),再依次輸出C(n, 1)、C(n, 2)、...、C(n, n),可參考http://blog.csdn.net/zhaojinjia/article/details/9320475
思路3:字符串“abc”與1-7的二進制數進行與運算,每次輸出二進制爲1的那幾位字符即可。
思路1代碼如下:
import java.util.*;
public class CodeThree{
public static void main(String[] args){
String str = "abc";
fun3(new StringBuffer(str), str.length());
}
public static void fun3(StringBuffer sbur, int len){
System.out.println(sbur);
if(len<=1){
return;
}else{
for(int i=0;i<len;i++){
StringBuffer temp = new StringBuffer(sbur);
sbur.deleteCharAt(i);
fun3(sbur,len-1);
sbur = temp;
}
}
}
}
思路3代碼如下:
public class CodeFour{
public static void main(String[] args){
String str = "abc";
fun4(str, str.length());
}
public static void fun4(String str, int len){
int N = (int)Math.pow(2,len);
for(int i=1;i<N;i++){
String temp = Integer.toBinaryString(i);
while(temp.length()<len){
temp = "0"+temp;
}
System.out.println(temp);
char[] arr = new char[len];
for(int j=0;j<len;j++){
if(temp.charAt(j)=='1'){
arr[j]=str.charAt(j);
}
}
System.out.println(String.valueOf(arr));
}
}
}
對遞歸的一點認識:
1、分析問題發現直接採用多個for循環能夠輸出想要的結果,但是for循環的個數與字符串的長度相關,字符串越長for 循環越多,這種情況就無法直接顯示的寫多個for循環,可以考慮採用遞歸的結構。
2、遞歸是不斷降低問題維度的過程,重點在於如何將n問題降低到n-1問題,然後不斷調用自身,而不是直接考慮n問題降低到1問題。
3、編寫遞歸程序時,可以將問題倒過來考慮,首先找到最簡單的情況作爲終止條件,然後逐步擴大分析。
4、在遞歸程序中,最好不要使用”n++“等自加語句,子情況遞歸時直接fun(n+1)即可。