排列组合

给定一个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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值