全排列与字典序排列

来源:互联网 发布:2016淘宝店铺重开技术 编辑:程序博客网 时间:2024/04/29 18:24

首先,全排列是一个比较简单的问题,但我却没有真正的去实现过全排列。

让我独自思考全排列的话,如将 “ 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;}




0 0
原创粉丝点击