【转帖】字符串的排列

      转帖:http://zhedahht.blog.163.com/blog/static/254111742007499363479/

    题目:输入一个字符串,打印出该字符串中字符的所有排列,如字符串abc,则输出由字符abc所能排列出的所有字符串abcacbbacbcacabcba

    分析:这是一道很好的考查对递归理解的编程题,因此在过去一年中频繁出现在各大公司的面试、笔试题中。

    以三个字符abc为例来分析一下求字符串排列的过程。首先我们固定第一个字符a,求后面两个字符bc的排列。当两个字符bc的排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列。现在是把c放到第一位置的时候了。记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍然是和原先处在第一位置的a交换,我们在拿c和第一个字符交换之前,先要把ba交换回来。在交换ba之后,再拿c和处在第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符ba的排列。

    既然我们已经知道怎么求三个字符的排列,那么固定第一个字符之后求后面两个字符的排列,就是典型的递归思路了。基于前面的分析,我们可以得到如下的参考代码:

 1 void Permutation(char* pStr, char* pBegin);  //函数声明
2
3 /////////////////////////////////////////////////////////////////////////
4 // Get the permutation of a string,
5 // for example, input string abc, its permutation is
6 // abc acb bac bca cba cab
7 /////////////////////////////////////////////////////////////////////////
8 void Permutation(char* pStr)
9 {
10 Permutation(pStr, pStr);
11 }
12
13 /////////////////////////////////////////////////////////////////////////
14 // Print the permutation of a string,
15 // Input: pStr - input string
16 // pBegin - points to the begin char of string
17 // which we want to permutate in this recursion
18 /////////////////////////////////////////////////////////////////////////
19 void Permutation(char* pStr, char* pBegin)
20 {
21 if(!pStr || !pBegin) return;
22
23 // if pBegin points to the end of string,
24 // this round of permutation is finished,
25 // print the permuted string
26 if(*pBegin == '\0') {
27 printf("%s\n", pStr);
28 }
29 // otherwise, permute string
30 else {
31 for(char* pCh = pBegin; *pCh != '\0'; ++ pCh)
32 {
33 // swap pCh and pBegin
34 char temp = *pCh;
35 *pCh = *pBegin;
36 *pBegin = temp;
37
38 Permutation(pStr, pBegin + 1);
39
40 // restore pCh and pBegin
41 temp = *pCh;
42 *pCh = *pBegin;
43 *pBegin = temp;
44 }
45 }
46 }

void my_permutation(char *str, int len, int index)
{
if (str == NULL) return;
if (index == len) cout << str << endl;
for (int i = index; i < len; i++) {
SWAP(str[i], str[index]);
my_permutation(str, len, index+1);
SWAP(str[i], str[index]);
}
}

    扩展1:如果不是求字符的所有排列,而是求字符的所有组合,应该怎么办呢?当输入的字符串中含有相同的字符串时,相同的字符交换位置是不同的排列,但是同一个组合。举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc

    扩展2:输入一个含有8个数字的数组,判断有没有可能把这8个数字分别放到正方体的8个顶点上,使得正方体上三组相对的面上的4个顶点的和相等。

——————————————————————————————————————————————————————————————

    延伸:八皇后问题

    题目:在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行、同一列或者同一对角斜线上,求总共有多少种摆法。

    八个皇后的任意两个不能处在同一行,即每一个皇后占据一行,可定义一个数组ColumnIndex[8],数组中第i个数字表示位于第i行的皇后的列号。先把ColumnIndex的八个数字分别用0-7初始化,接下来我们要做的事情就是对数组ColumnIndex做全排列。由于我们是用不同的数字初始化数组中的数字,因此任意两个皇后肯定不同列。我们只需要判断得到的每一个排列对应的八个皇后是不是在同一对角斜线上,也就是数组的两个下标ij,是不是i-j==ColumnIndex[i]-Column[j]或者j-i==ColumnIndex[i]-ColumnIndex[j]

int g_number = 0;

void EightQueen()
{
const int queens = 8;
int ColumnIndex[queens];
for(int i = 0; i < queens; ++ i)
ColumnIndex[i] = i;
Permutation(ColumnIndex, queens, 0);
}

void Permutation(int ColumnIndex[], int length, int index)
{
if(index == length) {
if(Check(ColumnIndex, length)) {
++ g_number;
PrintQueen(ColumnIndex, length);
}
}
else {
for(int i = index; i < length; ++ i) {
SWAP(ColumnIndex[i], ColumnIndex[index]);
Permutation(ColumnIndex, length, index + 1);
SWAP(ColumnIndex[i], ColumnIndex[index]);
}
}
}

bool Check(int ColumnIndex[], int length) //判断全排列是否满足条件
{
for(int i = 0; i < length; ++ i) {
for(int j = i + 1; j < length; ++ j) {
if((i - j == ColumnIndex[i] - ColumnIndex[j])
|| (j - i == ColumnIndex[i] - ColumnIndex[j]))
return false;
}
}
return true;
}

void PrintQueen(int ColumnIndex[], int length)
{
printf("Solution %d\n", g_number);
for(int i = 0; i < length; ++i)
cout << ColumnIndex[i] << " ";
cout << endl;
}

    虽错综复杂,但脉络清晰~




posted on 2011-10-26 10:35  白草黒尖  阅读(264)  评论(0)    收藏  举报