关于全排列--手写next_permutation()函数

求1~n的全排列,有两种方法,dfs和借助next_permutation()函数,这里我浅谈后者。

next_permutation()原型是bool next_permutation(iterator start,iterator end),在c++库<algorithm>中,既找数组的下一个排列,这里的数组可以是整型、字符型等。

代码实现1~3的全排列:

#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;  
}

括号内的范围类似sort函数,sort(首地址,首地址+个数),考虑升降幂的话加一个cmp函数来判断。

这样直接用很简单,但是我想讲一讲手写next_permutaion.

核心就是:如何找下一个排列,规律为何?

清楚一点:每次next_permutation确定的下一个排列实则只交换了两个数,如原来是1234,下一个为1243,交换了4和3.

如9237740这个排列,下一个是谁?如何做交换?

核心:从后往前找,找一对递增的相邻数,如40不是递增,74也不是,37是,记这对递增数的前一个数3为关键数,从后面找比它大的数中的最小数4,做交换变为7247730,再将4后面做一次颠倒得到7240377,即9237740的下一个全排列数。

换一种理解,我们从后找发现7740是逆序,无法通过交换得到一个比原数大的数,往前走,37740非逆序,将3和4交换,变为47730,做一次翻转,得到9237740,正好是9237740的下一个排列数。

看代码吧:

#include <iostream>  
using namespace std;

const int N = 20;
int a[N];
int n;

void swap(int *a,int *b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

void reverse(int *a,int *b)
{
    while (a<b) {
        swap(a++,b--);
    }
}

bool next_per()
{
    int *end = a + 1 + n; // 尾指针 
    if (end == a + 1) return false; // n = 0 
    end--;
    int *p,*pp,*find;
    p = end;// p从后往前遍历
    while (p != a + 1) {  
        pp = p; // pp为p右相邻指针 
        p--;
        if (*p < *pp) {// 相邻递增  
            find = end; // 从后往前 找最小的大于*p的数  同时也是第一个大于*p 的数
            while (*find <= *p)    find--;
            
            swap(p,find);// 交换  
            reverse(pp,end); // 翻转 
            return true; 
        }
    }
    return false;
    
}

int main()
{
    cin>>n;
    for (int i = 1; i <= n;i++) {
        a[i] = i;// 初始化
    }
    do{
        for (int i = 1; i <= n; i++)
        cout<<a[i];
        cout<<"\n";
    }while(next_per());
    
    return 0;
}

 好了,手写全排列到这了,dfs以后再处吧,有什么问题也可以在下面留言,一起探讨下,谢谢啦~

上一篇:CF1470E Strange Permutation


下一篇:Permutations