给定一个n个不同元素的集合,列出所有的排列方式。例如:集合{a, b, c}的所有排列方式如下:
{a, b, c}, {a, c, b}, {b, a, c}, {b, c, a}, {c, a, b}, {c, b, a}。
n个不同元素的排列方式一共有n!种,这里提供一种递归的方法来解决这个问题。
令E = {e1, e2, ......, en}表示n个不同元素的集合,Ei为E中移去元素ei后的集合,perm(X)表示集合X中元素的排列方式,ei.perm(X)表示在perm(X)中的每个排列方式的前面加上ei后得到的排列方式。例如:E = {a, b, c},那么E1 = {b ,c},perm(E1) = (bc, cb),e1.perm(E1) = (abc, acb)。
对于递归的基本部分,采用n = 1。当只有一个元素时,只可能产生一种排列方式,所以perm(E) = (e),其中e是E中的唯一元素。当n > 1时,perm(E) = e1.perm(E1) + e2.perm(E2) + e3.perm(E3) + ... + en.perm(En)。这种递归定义形式是采用n个perm(X)来定义perm (E), 其中每个X包含n - 1个元素。至此,一个完整的递归定义所需要的基本部分和递归部分都已完成。
当n = 3 && E = {a, b, c}时,按照前面的递归定义可得perm(E) = a.perm({b, c}) + b.perm({a, c}) + c.perm({a, b})。
同样,按照递归定义有perm({b, c}) = b.perm({c} ) + c.perm({b}), 所以
a.perm({b, c}) = ab.perm({c}) + ac.perm({b}) = ab.c + ac.b = (abc, acb)
同理可得:
b.perm({a, c}) = ba.perm({c}) + bc.perm({a}) = ba.c + bc.a = (bac, bca)
c.perm({a, b}) = ca.perm({b}) + cb.perm({a}) = ca.b + cb.a = (cab, cba)
所以,perm (E) = (abc, acb, bac, bca, cab, cba)
注意:a.perm({b, c})实际上包含两个排列方式:abc和acb,a是它们的前缀,perm({b, c})是它们的后缀。同样地,ac.perm({b})表示前缀为ac、后缀为perm ( {b}) 的排列方式。
把上述perm (E) 的递归定义转变成一个C++ 函数,这段代码输出所有前缀为list[0:k-1], 后缀为list[k:m]的排列方式。调用Perm(list, 0, n-1) 将得到list[0:n-1] 的所有n! 个排列方式,在该调用中,k=0, m=n-1,因此排列方式的前缀为空,后缀为list[0: n-1] 产生的所有排列方式。当k=m时,仅有一个后缀list[m],因此list[0:m]即是所要产生的输出。当k<m时,先用list[k]与list[k:m]中的每个元素进行交换,然后产生list[k+1: m] 的所有排列方式,并用它作为list[0: k] 的后缀。具体实现代码如下:
#include <iostream>
using namespace std;
template <class T>
inline void Swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
//生成list[k:m]的所有排列方式
template<class T>
void Perm(T list[], int k, int m)
{
int i;
if (k == m)
{
for (i = 0; i <= m; i++)
cout << list [i];
cout << endl;
}
else
{
for (i = k; i <= m; i++)
{
Swap(list[k], list[i]);
Perm(list, k + 1, m);
Swap(list[k], list[i]);
}
}
}
int main(int argc, char *argv[])
{
char str[10];
cout << "请输入一个字符数组:";
cin >> str;
Perm(str, 0, strlen(str) - 1);
system("PAUSE");
return EXIT_SUCCESS;
}