下一个排列-cnblog

下一个排列

题意:给定一个长度为\(n\)的排列\(p\),现在需要求出\(p\)的下一个排列(字典序大于当前排列的最小字典序排列)。

比如\([1,3,4,2]\),我们去找大于这个排列的下一个排列,根据字典序比较规则,越靠前,且数字越大,字典序越大。

那么显然我们考虑一个后缀\(i+1\),如果后面是一个完全降序的序列,那么调整这个后缀显然无法得到一个字典序更大的排列,所以我们需要从后往前找到第一个\(a[i]<a[i+1]\),我们需要从后缀中找到一个数来替换\(a[i]\),同时为了使得增幅最小,所以我们从后缀中找到大于\(a[i]\)的最小的\(a[j]\),然后交换\(a[i],a[j]\),之后显然我们可以将后缀\(i+1\)逆序一下,这样就可以得到下一个更小的排列。

考虑这个例子\([3,5,4,2,1]\),显然后缀\([5,4,2,1]\)无法得到下一个排列,我们只能增大第一个位置,那么找到第一个大于\(3\)的数4,这时候交换这两个,得到\([4,5,3,2,1]\),显然我们可以将\([5,3,2,1]\)重排得到\([1,2,3,5]\),这样得到下一个排列\([4,1,2,3,5]\)

我们再考虑一下如何得到上一个排列呢,是不是也就是这个的逆过程:

  • 找到一个递增的后缀,之后将其逆序,\([4,5,3,2,1]\)
  • 之后找到小于4的最大的那个,将其进行交换得到\([3,5,4,2,1]\)
  • 这样就得到了上一个排列;
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
bool next_permutaion(vector<int>& a){
    int n=a.size();
    int i=n-2;
    while(i>=0&&a[i]>=a[i+1]){
        i--;
    }
    if(i<0) return false;
    //找到了一个a[i]>a[i+1]
    
    int j=n-1;
    while(j>i&&a[j]<=a[i]){
        j--;
    }
    swap(a[i],a[j]);
    reverse(a.begin()+i+1,a.end());
    return true;
}

bool prev_permutation(vector<int>& a){
    int n = a.size();
    int i = n - 2;
    // 找第一个 a[i] > a[i+1]
    while(i >= 0 && a[i] <= a[i+1]){
        i--;
    }
    if(i < 0) return false;

    int j = n - 1;
    while(j > i && a[j] >= a[i]){
        j--;
    }
    swap(a[i], a[j]);
    reverse(a.begin() + i + 1, a.end());
    return true;
}

int main(){
    int n;
    cin >> n;
    vector<int> p(n);
    for(int i=0;i<n;i++){
        cin >> p[i];
    }
    do{
        for(int i=0;i<n;i++){
            cout << p[i] << " ";
        }
        cout << endl;
    }while(next_permutation(p));
    return 0;
}

而要得到当前排列的下\(k\)个排列,我们可以使用康托展开和逆康托展开,先得到当前排列的排名,之后加上\(k\),最后将排名转化成排列。

posted @ 2026-07-27 22:37  alij  阅读(5)  评论(0)    收藏  举报