剑指Offer----面试题28:字符串的排列 & 去重

来源:互联网 发布:一个程序员的奋斗史txt 编辑:程序博客网 时间:2024/05/16 07:33

题目:


输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则打印输出字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab、cba。

分析:

首先求得所有可能出现在第一个位置上的字符,即把第一个字符和后边所有的字符交换;
固定第一个字符,求后面所有字符的排列。

如abc:
第一步:将第一行数据分别和第二行和第三行数据交换
abcbaccab   
第二步:保持第一列不变,将第二行数据分别和第三行数据交换
acbbcacba
交换完毕,显然,要用到递归的知识。

方法一:


源代码如下:
#include<iostream>using std::endl;using std::cout;void Permutation(char *str);void Permutation(char *str, char *begin);void Permutation(char *str){if (str == NULL){cout << "Thr array is empty" << endl;return;}cout << str << "的排列为:";Permutation(str, str);}void Permutation(char *str, char *begin){if (*begin == '\0')cout << str << "  ";else{for (char *pCh = begin; *pCh != '\0'; ++pCh){//交换第一个字符和当前所指向的字符char temp = *pCh;*pCh = *begin;*begin = temp;Permutation(str, begin + 1);//变回原来的形式temp = *pCh;*pCh = *begin;*begin = temp;}}}void test11(){cout << "\t=======测试空指针========" << endl;Permutation(NULL);}void test12(){cout << "\t=======测试只有分隔符的指针========" << endl;char ch[] = "";Permutation(ch);}void test13(){cout << "\t=======测试只有一个元素的指针========" << endl;char ch[] = "a";Permutation(ch);}void test14(){cout << "\t=======测试只有多个元素的指针========" << endl;char ch[] = "abc";Permutation(ch);}void test15(){cout << "\t=======测试只有多个元素的指针========" << endl;char ch[] = "abcd";Permutation(ch);}int main(){test11();cout << endl;test12();cout << endl;test13();cout << endl;test14();cout << endl;test15();cout << endl;system("pause");return 0;}

运行结果:

        =======测试空指针========Thr array is empty        =======测试只有分隔符的指针========的排列为:        =======测试只有一个元素的指针========a的排列为:a        =======测试只有多个元素的指针========abc的排列为:abc  acb  bac  bca  cba  cab        =======测试只有多个元素的指针========abcd的排列为:abcd  abdc  acbd  acdb  adcb  adbc  bacd  badc  bcad  bcda  bdcabdac  cbad  cbda  cabd  cadb  cdab  cdba  dbca  dbac  dcba  dcab  dacb  dabc请按任意键继续. . .


方法二:


原理和方法一相同,但是代码的实现方式略有不同。

源代码如下:
#include<iostream>#include<cstdlib>#include<cstdio>using std::cout;using std::endl;int num = 1;  //局部静态变量,用来统计全排列的个数  //k表示当前选取到第几个数,m表示公有多少个数void Permutation3(char *pStr, int k, int m){if ((pStr == NULL) || (m == 0))return;if (k == m){printf("第%d个排列\t%s\n", num++, pStr);}else{for (int i = k; i < m; i++){std::swap(*(pStr + k), *(pStr + i));Permutation3(pStr, k + 1, m);std::swap(*(pStr + k), *(pStr + i));}}}void test1111(){cout << "\t=======测试空指针========" << endl;Permutation3(NULL, 0, 0);}void test1222(){cout << "\t=======测试只有分隔符的指针========" << endl;char ch[] = "";Permutation3(ch, 0, 0);}void test1333(){cout << "\t=======测试只有一个元素的指针========" << endl;char ch[] = "a";Permutation3(ch, 0, 1);}void test1444(){cout << "\t=======测试只有多个元素的指针========" << endl;char ch[] = "abc";Permutation3(ch, 0, 3);}void test1555(){cout << "\t=======测试只有多个元素的指针========" << endl;char ch[] = "abcd";Permutation3(ch, 0, 4);}int main(){test1111();cout << endl;num = 1;test1222();cout << endl;num = 1;test1333();cout << endl;num = 1;test1444();cout << endl;num = 1;test1555();cout << endl;num = 1;system("pause");return 0;}

运行结果如下:
        =======测试空指针========        =======测试只有分隔符的指针========        =======测试只有一个元素的指针========第1个排列       a        =======测试只有多个元素的指针========第1个排列       abc第2个排列       acb第3个排列       bac第4个排列       bca第5个排列       cba第6个排列       cab        =======测试只有多个元素的指针========第1个排列       abcd第2个排列       abdc第3个排列       acbd第4个排列       acdb第5个排列       adcb第6个排列       adbc第7个排列       bacd第8个排列       badc第9个排列       bcad第10个排列      bcda第11个排列      bdca第12个排列      bdac第13个排列      cbad第14个排列      cbda第15个排列      cabd第16个排列      cadb第17个排列      cdab第18个排列      cdba第19个排列      dbca第20个排列      dbac第21个排列      dcba第22个排列      dcab第23个排列      dacb第24个排列      dabc请按任意键继续. . .

考虑去重问题:

由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这二个数就不交换了。如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数与第三个数是不相同的,交换之后得到221。与由122中第一个数与第三个数交换所得的221重复了。所以这个方法不行。

换种思维,对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。此时全排列生成完毕。

这样我们也得到了在全排列中去掉重复的规则——去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。

源代码:

#include<iostream>using namespace std;#include<assert.h>//在[nBegin,nEnd)区间中是否有字符与下标为pEnd的字符相等bool IsSwap(char* pBegin, char* pEnd){char *p;for (p = pBegin; p < pEnd; p++){if (*p == *pEnd)return false;}return true;}void Permutation4(char* pStr, char *pBegin){assert(pStr);if (*pBegin == '\0'){static int num = 1;  //局部静态变量,用来统计全排列的个数printf("第%d个排列\t%s\n", num++, pStr);}else{for (char *pCh = pBegin; *pCh != '\0'; pCh++)   //第pBegin个数分别与它后面的数字交换就能得到新的排列   {if (IsSwap(pBegin, pCh)){swap(*pBegin, *pCh);Permutation4(pStr, pBegin + 1);swap(*pBegin, *pCh);}}}}int main(void){char str[] = "abab";Permutation4(str, str);system("pause");return 0;}


运行结果如下:
第1个排列       abab第2个排列       abba第3个排列       aabb第4个排列       baab第5个排列       baba第6个排列       bbaa请按任意键继续. . .
0 0
原创粉丝点击