L'approche non ordonnée aura typiquement la complexité quadratique, sauf si les données sont triées à l'avance (par votre champ ID), auquel cas il serait linéaire et ne nécessiterait pas des recherches répétées par B.
struct CompareId
{
bool operator()(const MyType* a, const MyType* b) const
{
return a>ID < b->ID;
}
};
...
sort(A.begin(), A.end(), CompareId());
sort(B.begin(), B.end(), CompareId());
vector<MyType*> C;
set_difference(A.begin(), A.end(), B.begin(), B.end(), back_inserter(C));
une autre solution consiste à utiliser un conteneur ordonné comme std :: set avec CompareId utilisé pour l'argument modèle de StrictWeakOrdering. Je pense que ce serait mieux si vous avez besoin d'appliquer beaucoup d'opérations. Cela a son propre surcoût (être un arbre) mais si vous trouvez vraiment que c'est un problème d'efficacité, vous pouvez implémenter un allocateur de mémoire rapide pour insérer et supprimer des éléments très rapidement (note: ne faites cela que si vous définissez un goulot d'étranglement).
Avertissement: entrer dans un territoire un peu compliqué.
Il existe une autre solution qui peut être très rapide si applicable et vous n'avez jamais à vous soucier du tri des données. Fondamentalement, faire en sorte que tout groupe d'objets MyType partageant le même ID stocke un compteur partagé (ex: pointeur vers un entier non signé). Cela nécessitera la création d'une carte d'ID pour les compteurs et nécessitera l'extraction du compteur de la carte chaque fois qu'un objet MyType est créé en fonction de son ID. Puisque vous avez des objets MyType avec des ID en double, vous ne devriez pas avoir à insérer sur la carte aussi souvent que vous créez des objets MyType (la plupart ne peuvent probablement récupérer qu'un compteur existant). En plus de cela, avoir un compteur global 'traversal' qui est incrémenté chaque fois qu'il est récupéré.
static unsigned int counter = 0;
unsigned int traversal_counter()
{
// make this atomic for multithreaded applications and
// needs to be modified to set all existing ID-associated
// counters to 0 on overflow (see below)
return ++counter;
}
Maintenant revenons à l'endroit où vous avez des vecteurs A et B qui stockent MyType *. Pour récupérer les éléments dans A qui ne sont pas dans B, nous appelons d'abord traversal_counter(). En supposant que c'est la première fois que nous l'appelons, cela nous donnera une valeur de traversée de 0.
Maintenant parcourir tous les objets MyType * dans B et définir le compteur partagé pour chaque objet de 0 à la valeur de traversée, 1.
itérez chaque MyType * objet A. ceux qui ont une valeur de compteur qui ne correspond pas à la valeur actuelle traversal (1) sont les éléments a qui ne figurent pas dans B.
Qu'est-ce qui se passe lorsque vous débordez le compteur de parcours? Dans ce cas, nous parcourons tous les compteurs stockés dans la carte d'ID et les remettons à zéro avec le compteur de parcours lui-même. Cela n'aura besoin de se produire qu'une fois dans environ 4 milliards de traversées si c'est un entier non signé de 32 bits.
Il s'agit de la solution la plus rapide que vous pouvez appliquer à votre problème. Il peut effectuer n'importe quelle opération en complexité linéaire sur des données non triées (et toujours, pas seulement dans les meilleurs cas, comme une table de hachage), mais cela introduit de la complexité, ne le considérez que si vous en avez vraiment besoin.
Vraisemblablement parce que vous demandez, ils ne sont pas triés par ID? – Cascabel
Quelle est leur taille? Quelle est l'importance de 'vector'? Pouvez-vous changer à 'set'? Plus de détails nécessaires pour fournir une bonne réponse. – Stephen
J'ai modifié mon post original pour inclure une solution encore plus rapide et qui peut fonctionner sur des vecteurs non triés. Cependant, c'est un peu compliqué. – stinky472