J'ai remarqué dans Effective STL que
vecteur est le type de séquence qui doit être utilisé par défaut.
Qu'est-ce que cela signifie? Il semble qu'ignorer l'efficacité vector
puisse tout faire.
Quelqu'un pourrait-il m'offrir un scénario qui vector
n'est pas une option réalisable mais list
doit être utilisé?
Réponses:
Situations où vous souhaitez insérer de nombreux éléments n'importe où, mais à la fin d'une séquence à plusieurs reprises.
Découvrez les garanties de complexité pour chaque type de conteneur:
Quelles sont les garanties de complexité des conteneurs standards?
la source
list
apush_front
vecteur:
liste:
En général, utilisez vector lorsque vous ne vous souciez pas du type de conteneur séquentiel que vous utilisez, mais si vous effectuez de nombreuses insertions ou suppressions vers et depuis n'importe où dans le conteneur autre que la fin, vous allez vouloir pour utiliser la liste. Ou si vous avez besoin d'un accès aléatoire, alors vous allez vouloir un vecteur, pas une liste. En dehors de cela, il y a naturellement des cas où vous allez avoir besoin de l'un ou l'autre en fonction de votre application, mais en général, ce sont de bonnes lignes directrices.
la source
reserve()
pour le réduire à O (1). L'ajout de nouveaux éléments à une liste (c'est-à-dire leur non-épissage) permet d'effectuer O (n) allocations de magasin gratuit.list
libère de la mémoire lorsque vous effacez des éléments, mais cevector
n'est pas le cas. Avector
ne réduit pas sa capacité lorsque vous réduisez sa taille, sauf si vous utilisez l'swap()
astuce.Si vous n'avez pas besoin d'insérer souvent des éléments, un vecteur sera plus efficace. Il a une bien meilleure localisation du cache du processeur qu'une liste. En d'autres termes, l'accès à un élément rend très probable que l'élément suivant soit présent dans le cache et puisse être récupéré sans avoir à lire la RAM lente.
la source
La plupart des réponses ici manquent un détail important: pourquoi?
Que voulez-vous garder dans le conteneur?
S'il s'agit d'une collection de
int
s, alorsstd::list
perdra dans chaque scénario, peu importe si vous pouvez réallouer, vous ne supprimez que par le devant, etc. Les listes sont plus lentes à parcourir, chaque insertion vous coûte une interaction avec l'allocateur. Il serait extrêmement difficile de préparer un exemple, oùlist<int>
batvector<int>
. Et même dans ce cas, ildeque<int>
peut être préférable ou proche, et non pas simplement d'utiliser des listes, qui auront une surcharge de mémoire plus importante.Cependant, si vous avez affaire à de gros et moches blobs de données - et peu d'entre eux - vous ne voulez pas surallouer lors de l'insertion, et la copie en raison de la réallocation serait un désastre - alors vous pourriez peut-être être mieux avec un
list<UglyBlob>
quevector<UglyBlob>
.Pourtant, si vous passez à
vector<UglyBlob*>
ou mêmevector<shared_ptr<UglyBlob> >
, encore une fois - la liste sera en retard.Ainsi, le modèle d'accès, le nombre d'éléments cibles, etc. affecte toujours la comparaison, mais à mon avis - la taille des éléments - le coût de la copie, etc.
la source
list<T>
est la possibilité desplice
dans O (1) . Si vous avez besoin d'épissage à temps constant, la liste peut également être la structure de choix;)UglyBlob
- même un objet avec seulement quelques membres de la chaîne peuvent facilement être prohibitif de copier, de sorte que les réaffectations ne coûtera. Aussi: Ne négligez pas l'espace au-dessus de la croissance exponentielle d'unvector
objet de taille de quelques dizaines d'octets (si vous ne pouvez pasreserve
à l'avance).vector<smart_ptr<Large>>
vslist<Large>
- je dirais que si vous avez besoin d'un accès aléatoire aux éléments, cela a duvector
sens. Si vous n'avez pas besoin d'un accès aléatoire, lelist
semble plus simple et devrait fonctionner de la même manière.Une capacité spéciale de std :: list est l'épissage (liaison ou déplacement d'une partie ou d'une liste entière dans une liste différente).
Ou peut-être si votre contenu coûte très cher à copier. Dans un tel cas, il pourrait être moins coûteux, par exemple, de trier la collection avec une liste.
Notez également que si la collection est petite (et que le contenu n'est pas particulièrement coûteux à copier), un vecteur peut toujours surpasser une liste, même si vous l'insérez et l'effacez n'importe où. Une liste alloue chaque nœud individuellement, et cela pourrait être beaucoup plus coûteux que de déplacer quelques objets simples.
Je ne pense pas qu'il existe des règles très strictes. Cela dépend de ce que vous voulez principalement faire avec le conteneur, ainsi que de la taille que vous attendez du conteneur et du type contenu. Un vecteur l'emporte généralement sur une liste, car il alloue son contenu en un seul bloc contigu (il s'agit essentiellement d'un tableau alloué dynamiquement, et dans la plupart des cas, un tableau est le moyen le plus efficace de contenir un tas de choses).
la source
list::size
n'est pas nécessairement un temps constant. Voir stackoverflow.com/questions/228908/is-listsize-really-on et gcc.gnu.org/ml/libstdc++/2005-11/msg00219.htmlsplice
entre différentes listes est spécifiée comme une complexité linéaire etsize
est spécifiquement constante. Ainsi, pour accommoder les novices qui itèrentsize
ou quoi que ce soit, toute une classe d'algorithmes est hors de portée pour la STL, ou toute période de conteneurs «conforme».Eh bien, les élèves de ma classe semblent tout à fait incapables de m'expliquer quand il est plus efficace d'utiliser des vecteurs, mais ils ont l'air plutôt contents de me conseiller d'utiliser des listes.
Voilà comment je le comprends
Listes : Chaque élément contient une adresse vers l'élément suivant ou précédent, donc avec cette fonctionnalité, vous pouvez randomiser les éléments, même s'ils ne sont pas triés, l'ordre ne changera pas: c'est efficace si votre mémoire est fragmentée. Mais il a également un autre très gros avantage: vous pouvez facilement insérer / supprimer des éléments, car la seule chose que vous devez faire est de changer certains pointeurs. Inconvénient: pour lire un seul élément au hasard, vous devez passer d'un élément à un autre jusqu'à ce que vous trouviez la bonne adresse.
Vecteurs : Lorsque vous utilisez des vecteurs, la mémoire est beaucoup plus organisée comme les tableaux ordinaires: chaque nième éléments est stocké juste après (n-1) e élément et avant (n + 1) e élément. Pourquoi est-ce mieux que la liste? Parce qu'il permet un accès aléatoire rapide. Voici comment: si vous connaissez la taille d'un élément dans un vecteur et s'ils sont contigus en mémoire, vous pouvez facilement prédire où se trouve le n-ième élément; vous n'avez pas à parcourir tous les éléments d'une liste pour lire celui que vous voulez, avec vector, vous le lisez directement, avec une liste que vous ne pouvez pas. En revanche, modifier le tableau vectoriel ou changer une valeur est beaucoup plus lent.
Les listes sont plus appropriées pour garder une trace des objets qui peuvent être ajoutés / supprimés en mémoire. Les vecteurs sont plus appropriés lorsque vous souhaitez accéder à un élément à partir d'une grande quantité d'articles uniques.
Je ne sais pas comment les listes sont optimisées, mais vous devez savoir que si vous voulez un accès rapide en lecture, vous devez utiliser des vecteurs, car à quel point les listes de fixation STL sont bonnes, l'accès en lecture ne sera pas aussi rapide que vector.
la source
vector
causé par le changement de sa taille peut être lente? alors convenu, mais dans les cas où ilreserve()
peut être utilisé, cela évite ces problèmes.Fondamentalement, un vecteur est un tableau avec gestion automatique de la mémoire. Les données sont contiguës en mémoire. Essayer d'insérer des données au milieu est une opération coûteuse.
Dans une liste, les données sont stockées dans des emplacements de mémoire non liés. L'insertion au milieu n'implique pas la copie de certaines données pour faire de la place pour la nouvelle.
Pour répondre plus précisément à votre question je citerai cette page
la source
Chaque fois que vous ne pouvez pas faire invalider les itérateurs.
la source
deque
suffiraient.Lorsque vous avez beaucoup d'insertion ou de suppression au milieu de la séquence. par exemple un gestionnaire de mémoire.
la source
La préservation de la validité des itérateurs est l'une des raisons d'utiliser une liste. Un autre est lorsque vous ne voulez pas qu'un vecteur se réalloue lors de la poussée d'éléments. Cela peut être géré par une utilisation intelligente de reserve (), mais dans certains cas, il peut être plus facile ou plus pratique d'utiliser simplement une liste.
la source
Lorsque vous souhaitez déplacer des objets entre des conteneurs, vous pouvez utiliser
list::splice
.Par exemple, un algorithme de partitionnement de graphe peut avoir un nombre constant d'objets répartis récursivement entre un nombre croissant de conteneurs. Les objets doivent être initialisés une fois et restent toujours aux mêmes emplacements en mémoire. Il est beaucoup plus rapide de les réorganiser en reconnectant qu'en réallouant.
Edit: à mesure que les bibliothèques se préparent à implémenter C ++ 0x, le cas général de l'épissure d'une sous-séquence en une liste devient de plus en plus complexe avec la longueur de la séquence. C'est parce que
splice
(maintenant) doit itérer sur la séquence pour compter le nombre d'éléments qu'elle contient. (Parce que la liste doit enregistrer sa taille.) Le simple comptage et la nouvelle liaison de la liste est encore plus rapide que n'importe quelle alternative, et l'épissage d'une liste entière ou d'un seul élément sont des cas spéciaux avec une complexité constante. Mais, si vous avez de longues séquences à épisser, vous devrez peut-être chercher un meilleur conteneur à l'ancienne et non conforme.la source
La seule règle stricte où
list
doit être utilisée est l'endroit où vous devez distribuer des pointeurs aux éléments du conteneur.Contrairement à avec
vector
, vous savez que la mémoire des éléments ne sera pas réallouée. Si c'est le cas, vous pourriez avoir des pointeurs vers la mémoire inutilisée, ce qui est au mieux un grand non-non et au pire unSEGFAULT
.(Techniquement, un
vector
de*_ptr
fonctionnerait également, mais dans ce cas, vous émulezlist
donc c'est juste de la sémantique.)D'autres règles souples concernent les éventuels problèmes de performances liés à l'insertion d'éléments au milieu d'un conteneur, ce
list
qui serait préférable.la source
Rendez-le simple -
À la fin de la journée, lorsque vous ne savez pas choisir des conteneurs en C ++, utilisez cette image de l'organigramme (dites-moi merci): - entrez la description de l'image ici
Vecteur -
1. le vecteur est basé sur la mémoire contagieuse
2. le vecteur est le chemin à parcourir pour un petit ensemble de données
3. le vecteur est plus rapide lors de la traversée sur l'ensemble de données
4. la suppression d'insertion de vecteur est lente sur un énorme ensemble de données mais rapide pour les très petits
Liste -
1. La liste est basée sur la mémoire de tas
2. La liste est le chemin à parcourir pour un ensemble de données très volumineux
3. La liste est relativement lente pour parcourir un petit ensemble de données mais rapide pour un ensemble de données énorme
4. La suppression d'insertion de liste est rapide énorme ensemble de données mais lent sur les plus petits
la source
Les listes ne sont qu'un encapsuleur pour une double liste de liens dans stl, offrant ainsi la fonctionnalité que vous pourriez attendre de d-linklist, à savoir l'insertion et la suppression de O (1). Alors que les vecteurs sont une séquence de données contagieuse qui fonctionne comme un tableau dynamique.PS- plus facile à parcourir.
la source
Dans le cas d'un vecteur et d'une liste , les principales différences qui me viennent à l'esprit sont les suivantes:
vecteur
Un vecteur stocke ses éléments dans une mémoire contiguë. Par conséquent, l'accès aléatoire est possible à l'intérieur d'un vecteur, ce qui signifie que l'accès à un élément d'un vecteur est très rapide car nous pouvons simplement multiplier l'adresse de base par l'index de l'élément pour accéder à cet élément. En fait, cela ne prend que O (1) ou un temps constant à cet effet.
Puisqu'un vecteur enveloppe un tableau, chaque fois que vous insérez un élément dans le vecteur (tableau dynamique), il doit se redimensionner en trouvant un nouveau bloc de mémoire contigu pour accueillir les nouveaux éléments, ce qui est coûteux en temps.
Il ne consomme pas de mémoire supplémentaire pour stocker des pointeurs vers d'autres éléments qu'il contient.
liste
Une liste stocke ses éléments dans une mémoire non contiguë. Par conséquent, l'accès aléatoire n'est pas possible à l'intérieur d'une liste, ce qui signifie que pour accéder à ses éléments, nous devons utiliser les pointeurs et parcourir la liste qui est plus lente par rapport au vecteur. Cela prend O (n) ou un temps linéaire qui est plus lent que O (1).
Puisqu'une liste utilise de la mémoire non contiguë, le temps nécessaire pour insérer un élément à l'intérieur d'une liste est beaucoup plus efficace que dans le cas de son homologue vectoriel car la réallocation de la mémoire est évitée.
Il consomme de la mémoire supplémentaire pour stocker des pointeurs vers l'élément avant et après un élément particulier.
Donc, en gardant ces différences à l'esprit, nous considérons généralement la mémoire , les accès aléatoires fréquents et l' insertion pour décider du gagnant du vecteur par rapport à la liste dans un scénario donné.
la source
La liste est une liste double liée, il est donc facile d'insérer et de supprimer un élément. Nous devons simplement changer les quelques pointeurs, alors qu'en vecteur si nous voulons insérer un élément au milieu, chaque élément doit ensuite se déplacer d'un index. De plus, si la taille du vecteur est pleine, il doit d'abord augmenter sa taille. C'est donc une opération coûteuse. Ainsi, chaque fois que des opérations d'insertion et de suppression doivent être effectuées plus souvent dans une telle liste de cas, il convient d'utiliser.
la source