web-dev-qa-db-fra.com

Pourquoi la STL C ++ ne fournit-elle aucun conteneur "arborescence"?

Pourquoi la STL C++ ne fournit-elle aucun conteneur "arborescence" et quelle est la meilleure chose à utiliser à la place?

Je souhaite stocker une hiérarchie d'objets sous forme d'arborescence, plutôt que d'utiliser une arborescence comme amélioration des performances ...

353
Roddy

Vous pouvez utiliser un arbre pour deux raisons:

Vous voulez reproduire le problème en utilisant une structure arborescente:
Pour cela, nous avons bibliothèque de graphes boostés

Ou vous voulez un conteneur qui a une arborescence comme des caractéristiques d'accès Pour cela, nous avons

Fondamentalement, les caractéristiques de ces deux conteneurs sont telles qu’elles doivent pratiquement être mises en œuvre à l’aide d’arbres (bien que cela ne soit pas réellement requis).

Voir aussi cette question: Implémentation de l'arborescence C

175
Martin York

Probablement pour la même raison qu'il n'y a pas de conteneur d'arbre dans boost. Il existe de nombreuses façons de mettre en œuvre un tel conteneur et il n’existe aucun moyen efficace de satisfaire tous les utilisateurs.

Quelques points à considérer:
- Le nombre d'enfants pour un nœud est-il fixe ou variable?
- Combien de temps système par nœud? - c’est-à-dire, avez-vous besoin de pointeurs parents, frères et sœurs, etc.
- Quels algorithmes à fournir? - différents itérateurs, algorithmes de recherche, etc.

En fin de compte, le problème est qu'un conteneur d'arbre, qui serait suffisamment utile pour tout le monde, serait trop lourd pour satisfaire la plupart des utilisateurs. Si vous recherchez quelque chose de puissant, Boost Graph Library est essentiellement un sur-ensemble de ce à quoi une bibliothèque d'arbres peut être utilisée.

Voici quelques autres implémentations d'arbres génériques:
- l'arbre.hh de Kasper Peeters
- forêt d'Adobe
- core :: tree

89
Greg Rogers

La philosophie de la STL consiste à choisir un conteneur en fonction de garanties et non en fonction de la manière dont le conteneur est implémenté. Par exemple, votre choix de conteneur peut être basé sur un besoin de recherches rapides. Malgré tout, le conteneur peut être implémenté comme une liste unidirectionnelle - tant que la recherche est très rapide, vous serez heureux. C'est parce que vous ne touchez pas de toute façon aux internes, vous utilisez des itérateurs ou des fonctions membres pour l'accès. Votre code n'est pas lié à la manière dont le conteneur est implémenté, mais à sa vitesse, à son ordre fixe et défini, à son efficacité en termes d'espace, etc.

50
wilhelmtell

"Je veux stocker une hiérarchie d'objets sous forme d'arborescence"

C++ 11 est venu et reparti et ils ne voyaient toujours pas la nécessité de fournir un std::tree, bien que l'idée soit venue (voir ici ). La raison pour laquelle ils n’ont pas ajouté ceci est peut-être qu’il est très facile de construire le vôtre par-dessus les conteneurs existants. Par exemple...

template< typename T >
struct tree_node
   {
   T t;
   std::vector<tree_node> children;
   };

Un simple parcours utiliserait la récursivité ...

template< typename T >
void tree_node<T>::walk_depth_first() const
   {
   cout<<t;
   for ( auto & n: children ) n.walk_depth_first();
   }

Si vous souhaitez conserver une hiérarchie et avec laquelle vous voulez que cela fonctionne algorithmes STL , la situation risque de se compliquer. Vous pouvez créer vos propres itérateurs et obtenir une certaine compatibilité. Cependant, de nombreux algorithmes n'ont tout simplement aucun sens pour une hiérarchie (tout ce qui modifie l'ordre d'une plage, par exemple). Même définir une plage dans une hiérarchie pourrait être une entreprise complexe.

47
nobar

Si vous recherchez une implémentation RB-tree, alors stl_tree.h pourrait vous convenir également.

42
systemsfault

std :: map est basé sur un arbre noir rouge . Vous pouvez également utiliser d'autres conteneurs pour vous aider à mettre en œuvre vos propres types d'arbres.

12
J.J.

D'une certaine manière, std :: map est un arbre (il doit avoir les mêmes caractéristiques de performances qu'un arbre binaire équilibré), mais il n'expose pas les autres fonctionnalités de l'arbre. La raison probable pour ne pas inclure une véritable structure de données arborescente était probablement juste une question de ne pas tout inclure dans la stl. Stl peut être considéré comme un framework à utiliser pour implémenter vos propres algorithmes et structures de données.

En général, s'il existe une fonctionnalité de bibliothèque de base que vous souhaitez, ce n'est pas dans stl, le correctif consiste à regarder BOOST .

Sinon, il y a un groupe de bibliothèquesout , selon les besoins de votre arborescence.

8
Eclipse

Tous les conteneurs STL sont représentés de manière externe sous forme de "séquences" avec un mécanisme d'itération. Les arbres ne suivent pas cet idiome.

6
Emilio Garavaglia

Celui-ci semble prometteur et semble être ce que vous cherchez: http://tree.phi-sci.com/

4
roffez

Parce que la STL n'est pas une bibliothèque "de tout". Il contient essentiellement les structures minimales nécessaires à la construction.

4
Paul Nathan

Je pense qu'il y a plusieurs raisons pour lesquelles il n'y a pas d'arbres stl. Principalement, les arbres sont une forme de structure de données récursive qui, comme un conteneur (liste, vecteur, ensemble), possède une structure fine très différente qui rend délicats les choix corrects. Ils sont également très faciles à construire sous forme de base en utilisant le STL.

Un arbre à racines finies peut être considéré comme un conteneur qui a une valeur ou une charge utile, disons une instance de classe A et une collection éventuellement vide d'arbres (sous-) enracinés; les arbres qui vident des sous-arbres ont la forme de feuilles.

template<class A>
struct unordered_tree : std::set<unordered_tree>, A
{};

template<class A>
struct b_tree : std::vector<b_tree>, A
{};

template<class A>
struct planar_tree : std::list<planar_tree>, A
{};

Il faut réfléchir un peu sur la conception des itérateurs, etc., et sur les opérations relatives aux produits et aux coproduits que l’on permet de définir et d’être efficaces entre les arbres. vraiment vide de toute charge utile dans le cas par défaut.

Les arbres jouent un rôle essentiel dans de nombreuses structures mathématiques (voir les articles classiques de Butcher, Grossman et Larsen; ainsi que les articles de Connes et de Kriemer pour des exemples de liens possibles et de la manière dont ils sont utilisés pour énumérer). Il n'est pas correct de penser que leur rôle consiste simplement à faciliter certaines autres opérations. Ils facilitent plutôt ces tâches en raison de leur rôle fondamental en tant que structure de données.

Cependant, en plus des arbres, il existe également des "co-arbres"; les arbres ont avant tout la propriété que si vous supprimez la racine, vous supprimez tout.

Considérez les itérateurs sur l’arbre, ils seraient probablement réalisés comme une simple pile d’itérateurs, à un nœud et à son parent, jusqu’à la racine.

template<class TREE>
struct node_iterator : std::stack<TREE::iterator>{
operator*() {return *back();}
...};

Cependant, vous pouvez en avoir autant que vous voulez; ils forment collectivement un "arbre", mais là où toutes les flèches vont dans la direction de la racine, ce co-arbre peut être itéré, bien que plusieurs itérateurs, vers un itérateur et une racine triviaux; Cependant, il ne peut pas être parcouru (il ne connaît pas les autres itérateurs) ni l'ensemble des itérateurs ne peut être supprimé sauf en gardant une trace de toutes les instances.

Les arbres sont incroyablement utiles, ils ont beaucoup de structure, ce qui constitue un sérieux défi pour obtenir l'approche définitivement correcte. À mon avis, c'est pourquoi elles ne sont pas implémentées dans le TSL. De plus, jadis, j’avais vu des gens devenir religieux et trouver l’idée d’un type de conteneur contenant des occurrences de son type propre - mais ils doivent y faire face - c’est ce que représente un type d’arbre - c’est un nœud contenant un collection éventuellement vide d'arbres (plus petits). Le langage actuel le permet sans problème, mais le constructeur par défaut de container<B> n'attribue pas d'espace sur le tas (ou ailleurs) pour un B, etc.

Pour ma part, je serais heureux si cela, sous une bonne forme, se retrouvait dans la norme.

2
tjl

OMI, une omission. Mais je pense qu'il y a de bonnes raisons de ne pas inclure d'arborescence dans le TSL. Il y a beaucoup de logique dans le maintien d'un arbre, qui est mieux écrit comme fonctions membres dans l'objet de base TreeNode. Lorsque TreeNode est encapsulé dans un en-tête STL, cela devient encore plus compliqué.

Par exemple:

template <typename T>
struct TreeNode
{
  T* DATA ; // data of type T to be stored at this TreeNode

  vector< TreeNode<T>* > children ;

  // insertion logic for if an insert is asked of me.
  // may append to children, or may pass off to one of the child nodes
  void insert( T* newData ) ;

} ;

template <typename T>
struct Tree
{
  TreeNode<T>* root;

  // TREE LEVEL functions
  void clear() { delete root ; root=0; }

  void insert( T* data ) { if(root)root->insert(data); } 
} ;
2
bobobobo