算法學習【5】字符串全排列

      在面試中經常遇到字符串全排列問題,現整理常見情況如下:

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)即可。


發佈了43 篇原創文章 · 獲贊 50 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章