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.
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.
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 Foo
où val2
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 <
.