Come creare un albero in C++?


6

Come si crea una struttura dati ad albero in C++ che utilizza iteratori anziché puntatori? Non sono riuscito a trovare nulla nell'STL in grado di farlo. Quello che vorrei fare è quello di essere in grado di creare e manipolare alberi come questo:

#include <iostream> 
#include <tree> 
using namespace std; 

int main() 
{ 
    tree<int> myTree; 

    tree<int>::iterator i = myTree.root(); 
    *i = 42; 

    tree<int>::iterator j = i.add_child(); 
    *j = 777; 
    j = j.parent(); 

    if (i == myTree.root() && i == j) cout << "i and j are both pointing to the root\n"; 

    return 0; 
} 

Grazie, tree.hh sembra essere proprio quello che stavo cercando.

Se questo è per ottenere il beneficio di una struttura dati in possesso arbitrarie tipi indice, ottimizzato per la ricerca e buona all'atto dell'inserimento quindi prendere in considerazione utilizzando una mappa.

Una mappa è un contenitore associativo ha garanzie di esecuzione identici a quelle di un albero: logaritmica ricerca, inserimento logaritmica, soppressione logaritmica, spazio lineare. All'interno sono spesso implementati come alberi rosso-neri, anche se questo è non è una garanzia. Tuttavia, come utente STL , tutto quello che ci si dovrebbe preoccupare è la garanzia di prestazioni degli algoritmi e delle strutture dati STL . Se sono implementati come alberi o piccoli uomini verdi non dovrebbe importare a voi.

Non sono sicuro se una mappa è ciò di cui ho bisogno, ma grazie per le informazioni. Mi ricorderò di usare le mappe ogni volta che è possibile, invece di implementare gli alberi.

5

Ecco tree.hh che è un po 'vicino a quello che vuoi fare, anche se un po' diverso.

Ecco un pezzo di codice estratto dal suo sito Web.

int main(int, char **) 
    { 
    tree<string> tr; 
    tree<string>::iterator top, one, two, loc, banana; 

    top=tr.begin(); 
    one=tr.insert(top, "one"); 
    two=tr.append_child(one, "two"); 
    tr.append_child(two, "apple"); 
    banana=tr.append_child(two, "banana"); 
    tr.append_child(banana,"cherry"); 
    tr.append_child(two, "peach"); 
    tr.append_child(one,"three"); 

    loc=find(tr.begin(), tr.end(), "two"); 
    if(loc!=tr.end()) { 
     tree<string>::sibling_iterator sib=tr.begin(loc); 
     while(sib!=tr.end(loc)) { 
     cout << (*sib) << endl; 
     ++sib; 
     } 
     cout << endl; 
     tree<string>::iterator sib2=tr.begin(loc); 
     tree<string>::iterator end2=tr.end(loc); 
     while(sib2!=end2) { 
     for(int i=0; i<tr.depth(sib2)-2; ++i) 
      cout << " "; 
     cout << (*sib2) << endl; 
     ++sib2; 
     } 
     } 
    } 

Ora che c'è di diverso? La tua implementazione è più semplice quando si tratta di aggiungere un nodo all'albero. Anche se la tua versione è indiscutibilmente più semplice, probabilmente lo sviluppatore di questa libreria voleva avere alcune informazioni accessibili senza sfogliare l'albero, come ad esempio la dimensione dell'albero.

Suppongo anche che non volesse memorizzare la radice su tutti i nodi per motivi di prestazioni. Quindi, se vuoi implementarlo a modo tuo, ti suggerisco di mantenere la maggior parte della logica e di aggiungere il collegamento all'albero genitore nell'iteratore e riscrivere un'appendice.


3

Perché vuoi farlo? Se questo è per scopi di apprendimento, allora puoi scrivere la tua struttura dati dell'albero. Se questo è per ottenere il beneficio di una struttura di dati con tipi di indici arbitrari, ottimizzati per la ricerca e validi all'inserimento, prendere in considerazione l'utilizzo di una mappa.

Una mappa è un contenitore associativo con garanzie di prestazioni identiche a quelle di un albero: ricerca logaritmica, inserimento logaritmico, cancellazione logaritmica, spazio lineare. Internamente sono spesso implementati come alberi rosso-neri, anche se non è una garanzia. Tuttavia, come utente STL, tutto ciò che ci si dovrebbe preoccupare è la garanzia delle prestazioni degli algoritmi STL e delle strutture dati. Non importa che siano implementati come alberi o piccoli uomini verdi.

Come nota a margine, non esiste una funzione root(). Tutti i contenitori STL hanno la funzione begin() che implementa l'inizio concettuale di un contenitore. Il tipo di iteratore restituito da tale funzione dipende dalle caratteristiche del contenitore.