c++中的全排列

next_permutation函数

 

    组合数学中经常用到排列,这里介绍一个计算序列全排列的函数:next_permutation(start,end),和prev_permutation(start,end)。这两个函数作用是一样的,区别就在于前者求的是当前排列的下一个排列,后一个求的是当前排列的上一个排列。至于这里的“前一个”和“后一个”,我们可以把它理解为序列的字典序的前后,严格来讲,就是对于当前序列pn,他的下一个序列pn+1满足:不存在另外的序列pm,使pn<pm<pn+1.

 

对于next_permutation函数,其函数原型为:

     #include <algorithm>

     bool next_permutation(iterator start,iterator end)

当当前序列不存在下一个排列时,函数返回false,否则返回true

 

同时,相对应的,上一个排列即为prev_permutation(int *begin, int *end)

 看如下代码:

#include <iostream>  
#include <algorithm>  
using namespace std;  
int main()  
{  
    int num[3]={1,2,3};  
    do  
    {  
        cout<<num[0]<<" "<<num[1]<<" "<<num[2]<<endl;  
    }while(next_permutation(num,num+3));  
    return 0;  
}  

运行结果如图:

注意:当我们把while(next_permutation(num,num+3))中的3改为2时,输出就变为了下图所示:

说明此时只针对1,2进行了全排列,有两个,后面的3没有变化,同时改变了数组前两个的值。

由此可以看出,next_permutation(num,num+n)函数是对数组num中的前n个元素进行全排列,同时并改变num数组的值。

另外,需要强调的是,next_permutation()在使用前需要对欲排列数组按升序排序,否则只能找出该序列之后的全排列数。

比如,如果数组num初始化为3,1,2,那么输出就变为了:

 

维基百科上全排列的实现:

循环法:

#include <iostream>
using namespace std;
bool arrsame(int* arr, int len, int num) {
    int i;
    for (i = 0; i < len; i++)
        if (arr[i] == num)
            break;
    return i != len;
}
bool next_perm(int* perm, const int k, const int n) {
    int i = k - 1;
    do
        perm[i]++;
    while (arrsame(perm, i, perm[i]) || (perm[i] >= n && i--));
    if (perm[0] >= n)
        return 0;
    for (int num = 0, seat = i + 1; seat < k; num++)
        if (!arrsame(perm, i + 1, num))
            perm[seat++] = num;
    return 1;
}
int main() {
    int n, k;
    cout << "perm(n,k):" << endl;
    cin >> n >> k;
    if (n < k || k <= 0)
        return 0;
    int* perm = new int[k];
    for (int i = 0; i < k; i++)
        perm[i] = i;
    do
        for (int i = 0; i < k; cout << ((++i < k) ? ',' : '\n'))
            cout << perm[i] + 1;
    while (next_perm(perm, k, n));
    delete[] perm;
    return 0;
}

递归法:

#include <bits/stdc++.h>
using namespace std;

struct prem {
    int len;
    vector<int> used, position;
    function<void(vector<int>&)> action;
    prem(int l = 0, function<void(vector<int>&)> a = [](vector<int>& position) {}) : len(l), used(l, -1), position(l), action(a) {}
    void run(int now = -1) {
        if (now == len - 1) {
            action(position);
            return;
        }
        int next = now + 1;
        for (int i = 0; i < len; i++) {
            if (used[i] == -1) {
                used[i] = next;
                position[next] = i;
                run(next);
                used[i] = -1;
            }
        }
    }
};
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int len = 4;
    prem p(len, [&](vector<int>& p) {
        for (int i = 0; i < len; i++) {
            cout << p[i] << " ";
        }
        cout << endl;
    });
    p.run();
    return 0;
}

 

next_permutation 可以自定义比较函数 例如:POJ 1256

题目中要求的字典序是:A'<'a'<'B'<'b'<...<'Z'<'z',所以在用函数之前必须得按照题目要求的进行排序

#include<iostream> //poj 1256 Anagram
#include<string.h>
#include<algorithm>
using namespace std;
int cmp(char a,char b) 
{
 if(tolower(a)!=tolower(b))//tolower 是将大写字母转化为小写字母.
 return tolower(a)<tolower(b);
 else
 return a<b;
}
int main()
{
 char ch[20];
 int n;
cin>>n;
 while(n--)
{
scanf("%s",ch);
sort(ch,ch+strlen(ch),cmp);
 do
{
printf("%s\n",ch);
}while(next_permutation(ch,ch+strlen(ch),cmp));
}
 return 0;
}

 

 

.

 

posted @ 2019-08-06 20:05  浅滩浅  阅读(20638)  评论(0编辑  收藏  举报