首先,全排列是一个比较简单的问题,但我却没有真正的去实现过全排列。
让我独自思考全排列的话,如将 “ 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; }