2013-06-11 13 views
5

Tworzę drzewo (zasadniczo drzewo prefiksów, ale dla liczb, a nie ciągów), które składa się z posortowanej listy krotek liczb ((1,1,2), (1,2,5), (2, 1,0) itp.), Z których każdy jest powiązany z jedną wartością skalarną (najprawdopodobniej int lub double). Ponieważ jest on budowany tylko raz, a następnie iterowany nad/przeszukiwany kilka razy, zamierzam użyć std :: vectors do przechowywania dzieci każdego węzła. Aby przeszukać drzewo, wystarczy wywołać std :: lower_bound, aby wykonać przeszukiwanie binarne na wektorze _children każdego węzła, który będzie zawierał std :: pairs każdego węzła i ich odpowiedni klucz. Jednak dolne węzły muszą zawierać wektor z parami składającymi się z ostatniego wpisu w każdej krotce i ich odpowiednich wartości, a zatem muszą być innego typu niż BranchNode. Kod wygląda następująco:C++: Oddzielne klasy dla węzłów gałęzi i liści?

class GenNode 
{ 
}; 

template<typename key_type,typename value_type> 
class BranchNode : GenNode 
{ 
    void insert(std::pair< std::vector<key_type> , value_type>); 
private: 
    std::vector< std::pair<key_type,GenNode*> > _children; 
}; 

template<typename key_type,typename value_type> 
class LeafNode : GenNode 
{ 
private: 
    std::vector< std::pair<key_type,value_type> > _children; 
}; 

Jednak jest to naprawdę brzydki, ponieważ obie klasy musi dziedziczyć z klasy GenNode bezużyteczną tak, że dzieci z każdej BranchNode mogą być inne BranchNodes lub LeafNodes ... jest tam lepszy sposób na zrobienie tego?

+0

"Węzeł" to liść do momentu dodania do niego pod-węzłów, a następnie jest gałęzią. Po prostu obiekty 'Node'. –

+0

Nie chcę tego robić ze względu na wydajność; Nie chcę, aby węzły liści miały puste wektory dla dzieci, które nadal przechowują "rozmiar = 0" i inne podobne elementy. –

+0

Istnieje dyskusja na temat zalet różnych implementacji drzewa w sekcji komentarzy tego wpisu na blogu: http : //math.andrej.com/2009/04/11/on-programming-language-design/ – Heatsink

Odpowiedz

2

Jeśli chcesz przechowywać oba typy w tym samym wektorze, potrzebujesz klas, które mają być powiązane (jeden odziedziczył inny lub oba odziedziczył po wspólnym przodku).

Posiadanie pustego wspólnego przodka może wydawać się brzydkie. Ale możesz uzyskać bardziej elegancki wygląd, jeśli umieścisz akcję wyszukiwania (a także inne iteracje/przetwarzanie) w klasie bazowej jako metodę wirtualną i zaimplementujesz ją w BranchNode i LeafNode (implementacje będą inne).

W tym przypadku klasa podstawowa powinna być również szablonem. Co to znaczy jest o klasę bazową jak:

template<typename key_type,typename value_type> 
class GenNode { 
public: 
    virtual value_type search(key_type key) = 0; 
}; 
+0

Skończyłem z wykorzystaniem tego podejścia do projektowania, umieszczając metody begin() i search() we wspólnym interfejsie. –

2

Tak, potrzebne są dwa różne typy. W rzeczywistości coś takiego (Leaf OR Node) nazywa się discriminated union.

Istnieje kilka sposobów jego implementacji, ale w C++ o wspólnej klasie bazowej z dwiema pochodnymi klasami jest prawdopodobnie najczęstsza.

Ciekawą alternatywą dla posiadania wspólnej podstawy jest użycie boost::variant z recursive wrapper. Jest to szczególnie interesujące, ponieważ unika stosowania wskaźników (i tym samym zajmuje się zarządzaniem pamięcią).

+1

Wzorzec klasy bazowej + wzór klas pochodnych jest czasem używany zamiast dyskryminowanych związków, ale w rzeczywistości nie jest to związek dyskryminowany. Różnica polega na tym, że dyskryminowanej unii nie można rozszerzyć o nowe warianty. Innymi słowy, dyskryminowane związki pozwalają twierdzić, że nigdy nie będzie węzła drzewa innego niż "Leaf', nie-' Branch ". Hierarchie klas nie gwarantują tego, ponieważ umożliwiają zdefiniowanie nowych podklas. – Heatsink

+0

@Heatsink Dobrze. Wyróżnione związki są zaimplementowane * przez * dziedziczenie. Wdrożenie nie jest doskonałe, jak powiedziałeś, ale nadal twierdzę, że to, co próbujesz zaimplementować razem z nimi w tym przypadku, jest w rzeczywistości dyskryminowanym związkiem. –