做算法练习遇到这样一道习题,如下:
编写带有下面声明的例程:
public void permute(String str);
private void permute(char[] str,int low,int high);
第一个方法是驱动程序,它会调用第二个方法并显示String str中字符的所有排列。
例如输入“abc”,则输出“abc”,”acb”,”bac”,”bca”,”cab”,”cba”。
我的想法如下,并没有完全按照题目要求,写出两个方法
/**
* 采用递归,打印n个字符全排列
* 先打印第一个字符,再打印n-1个字符的全排列
* 这个方法存在被打印字符缺少的bug
* @param str 需要打印的字符
*/
public void permute01(String str) {
int length = str.length();//字符串长度
String sonStr = null;//代表n-1个字符的字符串
//递归出口,打印最后一个字符并换行
if (length == 1) {
System.out.println(str);
}
else
{
//通过循环截取字符串的每个字符作为第一个字符打印
//并且将剩下的n-1个字符作为字符串传递给方法以作递归
for (int i = 0; i < length; i++) {
if (i == 0)
{
sonStr = str.substring(1);
} else if (i == length - 1) {
sonStr = str.substring(0, i);
} else {
sonStr = str.substring(0, i) + str.substring(i + 1);
}
//打印第一个字符
System.out.print(str.charAt(i));
//传递字符串作递归
permute01(sonStr);
}
}
}
这个方法的缺陷是打印时会缺少字符,例如,输入“abc”时,输出如下
abc
cb
bac
ca
cab
ba
然后网上百度到一个正确方法,如下:
//字符全排列打印驱动方法
public void permute02(String str)
{
char[] strArray = str.toCharArray();
permute(strArray,0, strArray.length - 1);
}
/**
*
* 采用递归,打印n个字符全排列
* 方法目的是为了得到在low与high之间字符的全排列
* 将字符数组中第一个字符依次与其他字符交换位置
* 然后将low推进一位,得到n-1个字符的全排列,以此得到所有字符全排列
* @param list 字符数组
* @param low 最低位
* @param high 最高位
*/
private void permute(char[] list,int low,int high)
{
int i;
if (low == high) {
StringBuffer cout = new StringBuffer();
for (i = 0; i <= high; i++)
cout.append(list[i]);
System.out.println(cout);
} else {
for (i = low; i <= high; i++) {
//1.交换低位位置
char temp = list[low];
list[low] = list[i];
list[i] = temp;
//2.进入下一级
permute(list, low + 1, high);
//3.复原位置
temp = list[low];
list[low] = list[i];
list[i] = temp;
}
}
}
这个方法没有问题,输入“abc”时,会输出正确排列:
abc
acb
bac
bca
cba
cab
将问题进行下对比和分析,总结如下:
1.我的方法之所以会缺少字符,是因为每当打印一个字符时,其对应多种n-1个字符的全排列,因此除了第一种n-1个字符排列有首字符对应,其他种排列无首字符对应。
而正确方法中,其将字符存储在数组中,并在出口处进行打印。在递归中,上级方法具备下级方法所具有的特征,我们不用担心前面的字符无法打印,因为递归总会进行到出口处,并且出口会将整个数组打印。而在我的方法中每个排列只打印自己的首字符,这是方法特征,因此所有级别方法均具备这个特征,也是只打印其排列的首字符,若是不同字符数的排列只有一种还好,当排列多于一种时,就会出现其他种排列缺少前面字符的状况。
2.再看正确方法,方法的运行过程是:
先交换位置,再进入递归,出来后复原位置。交换位置的目的是为了让所有的字符均有机会当首字符,那么这里有一个需要担心的是,进入递归后返回的数组排序和进入前的有保持一样吗?因为只有一样,才能保证每个字符都能被交换到首位置。这里我们可以从最低级别递归分析起,当low与high相差一位时,交换位置,进入递归,此时low等于high,直接到出口打印字符串,递归回来后复原位置,说明对于low与high相差一位,位置可以复原,正是因为递归各级之间方法的相似性,所以往上推理,每级均能被复原,保证进入递归后返回的数组排序和进入前的有保持一样。