Penso che gli alberi B + siano una buona struttura dati del contenitore ordinata per uso generale, anche nella memoria principale. Anche quando la memoria virtuale non è un problema, la compatibilità con la cache è spesso, e le strutture B + sono particolarmente adatte per l'accesso sequenziale: le stesse prestazioni asintotiche di una lista collegata, ma con una facilità di memorizzazione della cache vicina a un semplice array. Tutto questo e O (log n) cercano, inseriscono ed eliminano.
Gli alberi B + presentano tuttavia problemi, ad esempio gli elementi che si spostano all'interno dei nodi quando si inseriscono/eliminano, invalidando i puntatori a tali elementi. Dispongo di una libreria contenitore che esegue "manutenzione del cursore": i cursori si collegano al nodo foglia a cui fanno attualmente riferimento in un elenco collegato, in modo che possano essere corretti o invalidati automaticamente. Dato che raramente c'è più di uno o due cursori, funziona bene, ma è comunque un lavoro in più lo stesso.
Un'altra cosa è che l'albero B + è essenzialmente proprio quello. Immagino che sia possibile rimuovere o ricreare i nodi non fogliali a seconda che ne siano necessari o meno, ma con i nodi binari si ottiene molta più flessibilità. Un albero binario può essere convertito in una lista collegata e tornare indietro senza copiare i nodi - basta cambiare i puntatori quindi ricorda che lo stai trattando come una struttura di dati diversa ora. Tra le altre cose, ciò significa che O (n) è abbastanza facile unire gli alberi: convertire entrambi gli alberi in elenchi, unirli e poi riconvertirli in un albero.
Un'altra cosa è l'allocazione della memoria e la liberazione.In un albero binario, questo può essere separato dagli algoritmi - l'utente può creare un nodo quindi chiamare l'algoritmo di inserimento e le eliminazioni possono estrarre i nodi (staccarli dall'albero, ma non liberare la memoria). In un albero B o in un albero B +, ovviamente non funziona: i dati vivranno in un nodo a più voci. Scrivere metodi di inserimento che "pianificano" l'operazione senza modificare i nodi fino a quando non sanno quanti nuovi nodi sono necessari e che possono essere allocati è una sfida.
Rosso nero vs AVL? Non sono sicuro che faccia una grande differenza. La mia libreria ha una classe "strumento" basata su policy per manipolare i nodi, con metodi per elenchi double-linked, alberi binari semplici, alberi di diffusione, alberi rosso-nero e traci, comprese varie conversioni. Alcuni di questi metodi sono stati implementati solo perché mi annoiavo in un momento o nell'altro. Non sono sicuro di aver nemmeno testato i metodi di treap. La ragione per cui ho scelto gli alberi rosso-neri piuttosto che AVL è perché personalmente comprendo meglio gli algoritmi - il che non significa che siano più semplici, è solo un colpo di storia che mi è più familiare.
Un'ultima cosa: ho inizialmente sviluppato i miei contenitori per alberi B + come esperimento. È uno di quegli esperimenti che non sono mai finiti davvero, ma non è qualcosa che incoraggerei gli altri a ripetere. Se tutto ciò di cui hai bisogno è un contenitore ordinato, la risposta migliore è utilizzare quello fornito dalla tua libreria esistente, ad es. std :: map etc in C++. La mia libreria si è evoluta nel corso degli anni, ci è voluto del tempo per renderla stabile, e ho scoperto, relativamente di recente, che è tecnicamente non portatile (dipende da un po 'di comportamento non definito WRT offsetof).
Bene, io per prima cosa apprezzo questa domanda - attualmente presentata con una scelta di fastutil IntAVLTreeSet vs. IntRBTreeSet. – Yang