在面试中经常遇到字符串全排列问题,现整理常见情况如下:
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)即可。