首先,全排列是一个比较简单的问题,但我却没有真正的去实现过全排列。
让我独自思考全排列的话,如将 “ abcd“ 进行全排列,这种简单的全排列也能将我难住,因为真的没有考虑过这种问题。思考了一会,我只能给出以下比较麻烦的算法:
//字符串全排列
void printRE(char* str,int index,char s[],int length){
if(index == length)
printf("%s \n",str);
else{
bool exsist = false;
for(int i = 0 ; i < length;i++){
exsist = false;
for(int j= 0; j < index;j++){
if(s[i] == str[j]){
exsist = true;
break;
}
}
if(!exsist){
str[index] = s[i];
printRE(str,index+1,s,length);
}
}
}
}
void PrintAll(char s[],int length){
char* str = new char[length+1];
str[length] = '\0';
printRE(str,0,s,length);
delete[] str;
}
这里使用一个字符串数组str来当作栈使用,实现了递归中的回溯。这里对字符是否使用过的判断就是遍历栈中元素,看之前部分的字符串中是否已用某些字符。
其实这些天多看了许多算法许多思路后,反而陷入了一种不知道解题方法就完全不去思考的状态,唉,脑子坏掉拉。
这里更简单的一种思路是,全排列即从第1个字母开始,与之后的字母交换,然后递归的输出全部:
void printALLA(char* str,char *begin){
if('\0' == *begin)
printf("%s \n",str);
else{
for(char* p = begin;*p != '\0';p++){
swap(*p,*begin);
printALLA(str,begin+1);
swap(*p,*begin);//返回原来的状态。
}
}
}
这是递归的方法。
非递归一般来说,字符串全排列可以使用字典序排列来实现。
这里字典序排列的算法很有意思,要记一下:
对于进行字典序排序的字符串 str ,从右向做寻找第一个小于右边元素的点 str[i]; 然后在i到字符串末尾寻找一个大于str[i]的最小字符 str[j], 交换str[i] 和 str[j]. 并将i之后的子串颠倒,得到一个新的字典序排序的结果。之后在这个结果的基础上去寻找下一个排序。
这个算法是很有意思的,这样的精致的思路是如何产生的,我无法知道,但是我了解到这样做的用意。首先,一开始字符串是已经排序好的字典序的开始状态,然后每一次循环的开始,都是将字符串分为两段,左边为 未进行字典排序的子段,而右边是已经进行字典排序的子段,而且这个子段之前的字典序已经被遍历并输出,这里寻找第一个小于右边的元素 str[i] ,而这个顺序之前的字典序已被输出的情况下,且右边字段必定是从大到小排序的,这样右边子段的字典序也被全部输出,这也说明对于str[i] 之前和包括自身已经全部遍历,这时要将str[i]换一个更大的元素来进行排列,而这个数就是右边字段中大于str[i]的最小元素str[j],交换str[i] 和str[j] ,,遍历以str[j] 开始的新的字典序,而这时候将右边字段倒转,因为之前是从大到小排序的,交换后就是从小到大排序,而这也就是str[j]为开始时,右边子段的第一个字典序。如此反复即可输出全部的字典序。
然后简单的写出字典序排列的代码:
void printLexOrder(char s[],int length){
int charBarrel[128];
memset(charBarrel,0,128*4);
for(int i = 0 ; i < length;i++){
charBarrel[ s[i] ] ++;
}
int i,j;
i = 0;
char* str = new char[length+1];
str[length] = '\0';
for(j = 0 ; j < 128;j++){
while(charBarrel[j] > 0){
charBarrel[j]--;
str[i++] = j;
}
}
int min;//最小的大于i的字符 的下标
char* stack = new char[length];
int stacktop = 0;//使用一个栈来实现字符串的颠倒
printf("%s \n",str);
while(i > -1){
j = length-1;
i = j - 1;
while(str[i] >= str[j]){
i--;
j--;
if(i<0)
break;
}
if(i<0)break;//跳出两层循环
j = i+1;
min = j;
while(j<length){
if(str[j] > str[i] && str[j] <= str[min]){
min = j;
}
j++;
}
j = min;
stack[stacktop] = str[i];
str[i] = str[j];
str[j] = stack[stacktop];
for(j = i+1;j<length;j++)
stack[stacktop++] = str[j];
while(stacktop > 0)
str[++i] = stack[--stacktop];
printf("%s \n",str);
}
delete[] stack, str;
}