字符串全排列之递归算法

做算法练习遇到这样一道习题,如下:

编写带有下面声明的例程:

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相差一位,位置可以复原,正是因为递归各级之间方法的相似性,所以往上推理,每级均能被复原,保证进入递归后返回的数组排序和进入前的有保持一样。

    原文作者:递归算法
    原文地址: https://blog.csdn.net/swingzhou93/article/details/63687144
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞