web-dev-qa-db-fra.com

Moyen efficace d'obtenir le milieu (médiane) d'un std :: set?

std::set est un arbre trié. Il fournit les méthodes begin et end afin que je puisse obtenir le minimum et le maximum ainsi que lower_bound et upper_bound pour la recherche binaire. Mais que se passe-t-il si je veux que l’itérateur pointe l’élément central (ou l’un d’eux s’il ya un nombre pair d’éléments)?

Existe-t-il un moyen efficace (O(log(size)) not O(size)) de le faire?

{1} => 1
{1,2} => 1 or 2
{1,2,3} => 2
{1,2,3,4} => 2 or 3 (but in the same direction from middle as for {1,2})
{1,312,10000,14000,152333} => 10000

PS: Même question en russe.

11
Qwertiy

Selon la fréquence à laquelle vous insérez/supprimez des éléments par rapport à la moyenne/médiane, une solution plus efficace que la solution évidente consiste à conserver un itérateur persistant dans l'élément central et à le mettre à jour chaque fois que vous insérez/supprimez des éléments de l'ensemble. De nombreux cas Edge doivent être traités (nombre d'éléments impairs ou pairs, suppression de l'élément central, ensemble vide, etc.), mais l'idée de base est que, lorsque vous insérez un élément plus petit que l'élément intermédiaire actuel , votre itérateur du milieu peut avoir besoin de décrémenter, alors que si vous en insérez un plus grand, vous devez incrémenter. C'est l'inverse pour les déménagements.

Au moment de la recherche, il s’agit bien sûr de O (1), mais il a aussi essentiellement un coût O(1) à chaque insertion/suppression, c’est-à-dire O(N) après N insertions , qui doit être amorti sur un nombre suffisant de recherches pour le rendre plus efficace que le forçage brutal.

16
pmdj

Il sera O(size) obtenir le milieu d'un arbre de recherche binaire. Vous pouvez l'obtenir avec std::advance() comme suit:

std::set<int>::iterator it = s.begin();
std::advance(it, s.size() / 2);
6
Martin Broadhurst

Sachez que le std::set ne stocke PAS les valeurs en double. Si vous insérez les valeurs suivantes {1, 2, 3, 3, 3, 3, 3, 3, 3}, la médiane à récupérer est 2.

std::set<int>::iterator it = s.begin();
std::advance(it, s.size() / 2);
int median = *it;

Si vous souhaitez inclure les doublons lorsque vous considérez la médiane, vous pouvez utiliser std::multiset (la médiane de {1, 2, 3, 3, 3, 3, 3, 3, 3} serait 3):

std::multiset<int>::iterator it = s.begin();
std::advance(it, s.size() / 2);
int median = *it;

Si la seule raison pour laquelle vous voulez que les données soient triées soit pour obtenir la médiane, il vaut mieux utiliser un ancien vieux std::vector + std::sort à mon avis.

Avec un grand échantillon de test et plusieurs itérations, j'ai terminé le test en 5 secondes avec std::vector et std::sort et entre 13 et 15s avec std::set ou std::multiset. Votre kilométrage peut varier en fonction de la taille et du nombre de doublons que vous possédez.

0
Norgannon