STLアルゴリズムnext_permutationの原理と使用

出典:http://leonard1853.iteye.com/blog/1450085

1. next_permutationに遭遇しました(順列:シーケンスの意味)

今日、TC(SRM531-Division Two-Level One)で簡単な質問に遭遇しました。これは、昇順で配置されていない特定の配列の最小の辞書シーケンスを見つけることです(Aに含まれる場合、番号Aのシーケンスは辞書式順序でBよりも小さくなります)それらが異なる最初の位置の小さい数)。

解決策は非常に簡単です。つまり、配列を並べ替え(昇順)、最後から最初に交換可能な最初の位置を見つけます(番号が重複している可能性があるため)。

最後に、他の人の解決策を見てください。並べ替えた後、STLで関数next_permutaionを使用して、昇順ではない最初のシーケンスを直接見つけます。

2.next_permutationの実装原理

この関数は「STLソースコード分析」で見つかりました。原理の簡単な説明は次のとおりです。

STLには、next_permutationに加えて、関数prev_permutationもあります。これらは両方とも、順列と組み合わせを計算するために使用されます。前者は次の順列と組み合わせを見つけることであり、後者は前の順列と組み合わせを見つけることです。いわゆる「次へ」と「前へ」の本は、簡単な例を示しています。シーケンス{a、b、c}の場合、辞書シーケンスによれば、各要素は後者よりも小さく、aを修正した後、aはbcより大きいすべてが小さく、cがbより大きい、次のシーケンスが{a、c、b}、前のシーケンスの{a、c、b}が{a、b、c}である場合、同じことが推測できます。すべての6つのシーケンスは次のとおりです:{a、b、c}、{a、c、b}、{b、a、c}、{b、c、a}、{c、a、b}、{c、b 、a}、ここで、{a、b、c}には前の要素がなく、{c、b、a}には次の要素がありません。

next_permutationの関数プロトタイプは次のとおりです。

template<class BidirectionalIterator>  
bool next_permutation(  
      BidirectionalIterator _First,   
      BidirectionalIterator _Last  
);  
template<class BidirectionalIterator, class BinaryPredicate>  
bool next_permutation(  
      BidirectionalIterator _First,   
      BidirectionalIterator _Last,  
      BinaryPredicate _Comp  
 );  

2番目のオーバーロードされた関数の3番目のパラメーターの場合、デフォルトの比較順序は未満です。次のシーケンスが見つかった場合はtrueを返し、それ以外の場合はfalseを返します。

関数実装の原則は次のとおりです。

現在のシーケンスでは、テールからフロントまで2つの隣接する要素を探します。前者はiとしてマークされ、後者はiiとしてマークされi < iiとしてマークされます。次に、最後から別の要素jを探します。i<* jが満たされると、i番目の要素とj番目の要素が交換され、i番目の要素の後のすべての要素(iiを含む)が上下逆に並べ替えられます。次のシーケンスがアップしています。

コードは次のように実装されています。

template<class BidirectionalIterator>  
bool next_permutation(  
      BidirectionalIterator first,   
      BidirectionalIterator last  
)  
{
    
      
    if(first == last)  
        return false; //空序列  
  
    BidirectionalIterator i = first;  
    ++i;  
    if(i == last)  
        return false;  //一个元素,没有下一个序列了  
      
    i = last;  
    --i;  
  
    for(;;) {
    
      
        BidirectionalIterator ii = i;  
        --i;  
        if(*i < *ii) {
    
      
            BidirectionalIterator j = lase;  
            while(!(*i < *--j));  
  
            iter_swap(i, j);  
            reverse(ii, last);  
            return true;  
        }  
          
        if(i == first) {
    
      
            reverse(first, last);  //全逆向,即为最小字典序列,如cba变为abc  
            return false;  
        }  
    }  
  
}  

prev_permutationは実装に似ています。つまり、逆引き参照です。

3.next_permutationを使用します

質問を考えてみましょう。シーケンス{a、d、c、e、b}の次のシーケンスは何ですか?前の分析を使用して答えを導き出し、コードで検証してください。

ここでは、配列とベクトルを使用してシーケンスを表し、next_permutationを使用して次のシーケンスを取得します(コンパイル環境:Dev-C ++):

#include <cstdlib>  
#include <iostream>  
#include <algorithm>  
#include <vector>  
  
using namespace std;  
  
void TestArray()   
{
    
      
    char chs[] = {
    
    'a', 'd', 'c', 'e', 'b'};  
    int count = sizeof(chs)/sizeof(char);  
      
    next_permutation(chs+0, chs + count);  
      
    printf("TestArray:\n");  
    for(int i = 0; i < count; i++) {
    
      
            printf("%c\t", chs[i]);  
    }  
      
    printf("\n");  
}  
  
void TestVector()  
{
    
      
     char chs[] = {
    
    'a', 'd', 'c', 'e', 'b'};  
     int count = sizeof(chs)/sizeof(char);  
     vector<char> vChs(chs, chs + count);  
       
     next_permutation(vChs.begin(), vChs.end());  
       
     printf("TestVector:\n");  
     vector<char>::iterator itr;  
     for(itr = vChs.begin(); itr != vChs.end(); itr++) {
    
      
             printf("%c\t", *itr);  
     }  
     printf("\n");  
}  
  
int main(int argc, char *argv[])  
{
    
      
    TestArray();  
    printf("\n");  
    TestVector();  
      
    system("PAUSE");  
    return EXIT_SUCCESS;  
}  

4.まとめ

next_permutationとprev_permutationを使用して順列と組み合わせを見つけるのは非常に便利ですが、ヘッダーファイル#includeをインクルードすることを忘れないでください。

最後の順列には次の順列がありませんが、next_permutationを使用するとfalseが返されますが、このメソッドを使用すると、シーケンスは辞書シーケンスの最初になります。たとえば、cbaはabcになります。同じことがprev_permutationにも当てはまります。

おすすめ

転載: blog.csdn.net/weixin_43743711/article/details/115180065