web-dev-qa-db-fra.com

Y a-t-il un conteneur trié dans la STL?

Y a-t-il un conteneur trié dans la STL?

Ce que je veux dire, c'est ce qui suit: J'ai un std::vector<Foo>, où Foo est une classe personnalisée. J'ai aussi un comparateur d'une certaine sorte qui comparera les champs de la classe Foo.

Maintenant, quelque part dans mon code je fais:

std::sort( myvec.begin(), myvec.end(), comparator );

qui va trier le vecteur selon les règles que j'ai définies dans le comparateur.

Maintenant, je veux insérer un élément de la classe Foo dans ce vecteur. Si je pouvais, je voudrais juste écrire:

 mysortedvector.Push_back( Foo() );

et ce qui arriverait, c’est que le vecteur mettra ce nouvel élément en fonction du comparateur.

Au lieu de cela, je dois maintenant écrire:

myvec.Push_back( Foo() );
std::sort( myvec.begin(), myvec.end(), comparator );

ce qui est juste une perte de temps, puisque le vecteur est déjà trié et que tout ce dont j'ai besoin est de placer le nouvel élément de manière appropriée.

Maintenant, à cause de la nature de mon programme, je ne peux pas utiliser std::map<> car je n'ai pas de paires clé/valeur, mais un simple vecteur.

Si j’utilise stl::list, j’ai à nouveau besoin d’appeler sort après chaque insertion.

14
Igor

Oui, std::set , std::multiset , std::map et std::multimap sont tous triés à l'aide de std::less comme opération de comparaison par défaut. La structure de données sous-jacente utilisée est généralement un arbre de recherche binaire équilibré tel qu'un arbre rouge-noir. Ainsi, si vous ajoutez un élément à ces structures de données, puis que vous le parcourez, la sortie sera triée. La complexité de l'ajout de N éléments à la structure de données sera O (N log N), ou identique au tri d'un vecteur de N éléments à l'aide de tout type de complexité O (log N) commun.

Dans votre scénario spécifique, puisque vous n'avez pas de paires clé/valeur, std::set ou std::multiset est probablement votre meilleur choix.

35
Jason

J'aimerais développer Jason answer . Je conviens avec Jason que soit std::set ou std::multiset est le meilleur choix pour votre scénario spécifique. J'aimerais donner un exemple afin de vous aider à préciser davantage votre choix.

Supposons que vous ayez la classe Foo suivante:

class Foo {
public:
    Foo(int v1, int v2) : val1(v1), val2(v2) {};
    bool operator<(const Foo &foo) const { return val2 < foo.val2; }
    int val1;
    int val2;
};

Ici, Foo surcharge l'opérateur <. De cette façon, vous n'avez pas besoin de spécifier une fonction de comparaison explicite. En conséquence, vous pouvez simplement utiliser un std::multiset au lieu d'un stf:vector de la manière suivante. Il vous suffit de remplacer Push_back() par insert():

int main()
{
    std::multiset<Foo> ms;
    ms.insert(Foo(1, 6));
    ms.insert(Foo(2, 5));
    ms.insert(Foo(3, 4));
    ms.insert(Foo(1, 4));

    for (auto const &foo : ms)
        std::cout << foo.val1 << " " << foo.val2 << std::endl;

    return 0;
}

Sortie:

3 4
2 4
1 5
1 6

Comme vous pouvez le constater, le conteneur est trié par le membre val2 de la classe Foo, en fonction de l'opérateur <. Cependant, si vous utilisez std::set au lieu d'un std::multiset, vous obtiendrez une sortie différente:

int main()
{
    std::set<Foo> s;
    s.insert(Foo(1, 6));
    s.insert(Foo(1, 5));
    s.insert(Foo(3, 4));
    s.insert(Foo(2, 4));

    for (auto const &foo : s)
        std::cout << foo.val1 << " " << foo.val2 << std::endl;

    return 0;
}

Sortie:

3 4
1 5
1 6

Ici, le deuxième objet Fooval2 est 4 est manquant, car un std::set n'autorise que des entrées uniques. Si les entrées sont uniques est décidé en fonction de l'opérateur < fourni. Dans cet exemple, l'opérateur < compare les membres val2 entre eux. Par conséquent, deux objets Foo sont égaux, si leurs membres val2 ont la même valeur.

Ainsi, votre choix dépend du fait que vous souhaitiez ou non stocker des objets Foo pouvant être égaux en fonction de l'opérateur <.

Code sur Ideone

1
honk