问题描述:输入一个字符串,输出该字符串所有可能的排列方式(全排列);
问题解析:
字符串全排列问题算是一种找规律的问题,若果我们能找到生成第一个字符排列的方法,那么参考这个方法就可以依次生成其余的字符串排列,不过得强调一点,找到的这个方法必须得满足正确性、互异性和完整性,否则就不能称得上是全排列了。
按照每个人的不同思维习惯,字符串全排列的生成方法应该是多样的,有的人喜欢从后往前生成新的排列,有的人偏爱于依次将单个字符插入不同位置来生成新的序列,博主将用下面这个例子阐述一下自己的思路。
如上图所示,我们以 {a,b,c,d} 为例:
从第一列开始,我们将“a”与自身和其它字符依次交换位置(绿色为交换位置的两个字符),生成第二列中的四个新的子串;
到了第二列中,我们将第一位固定不动(橘黄色为固定不动的字符位),将剩下的三个字符视为新的字符串,新的字符串重复由第一列到第二列的操作。如“b”分别与“自身”、“c”、“d”进行位置交换,生成第三列;
到了第三列,固定不动的位数加一,即前两位不动,剩下的两个字符视为新的字符串,重复之前的字符交换操作,生成第四列;
到了第四列,固定不动的位数加一,前三位不再参与字符交换,剩下的一位字符再进行交换将毫无意义,排列结束;
至此,{a,b,c,d} 生成全部的字符排列。
程序设计:
有了好的思路,得想办法转化为可运行的程序化过程,如下:
上述排序算法中,随着单个字符与其它字符的依次交换,会存在一些固定不动的点,这些点是不参与下一轮字符交换的,那么我们只需要将可操作的片段传入下一步;
假设待排序字符串为“Str[n]”,初始可操作片段为整个字符串,我们将(0,n-1)传入下一步,并将第一个操作字符设置为指定交换字符;
设传入的代码片段为(m,n-1),若指定交换字符为“n-1”,则将“m-1,n-1”传给自己,并将“m”设为指定交换字符(在这里,如果“m-1”小于“0”,则跳到最后一步);若指定交换字符不为“n-1”,将“m”与指定的交换字符进行位置交换,将新生成的代码片段传入下一步;
设传入的代码片段为(m,n-1),若该可操作片段长度为“1”,即“m”与“n-1”同字符,输出“Str[n]”,并把(m-1,n-1)传回上一步,将“m-1”设为指定交换字符;若可操作片段长度大于“1”,则把(m+1,n-1)传回上一步,将“m+1”设为指定交换字符;
至此,已生成字符串“Str[n]”的全部排列。
这里,博主用的是一个回溯的思想,其实根据例题描述,那些交换字符生成的新的字符串在下一步中完全独立,因此可以考虑分治的设计思路,效率会更高。
代码展示:
#include <iostream>
using namespace std;
void CharSort(char *str, int start, int end)
{
char data;
int i;
if (start == end)
{
for (i = 0; i < end; i++)
cout << str[i];
cout << endl;
}
else
{
for (i = start; i < end; i++)
{
data = str[start];
str[start] = str[i];
str[i] = data;
CharSort(str, start + 1, end);
data = str[start];
str[start] = str[i];
str[i] = data;
}
}
}
int main()
{
char str[10];
cin >> str;
CharSort(str, 0, strlen(str));
return 0;
}
5.运行结果
(注:第一行“abcd”为开始时的输入部分,从第二行开始为程序输出部分)