为什么C ++ STL不提供任何“树”容器?

Why does the C++ STL not provide any “tree” containers?

为什么C++ STL不提供任何"树"容器,而最好使用什么呢?

我希望将对象的层次结构存储为树,而不是使用树作为性能增强…


使用树有两个原因:

您希望使用类似树的结构来镜像问题:为此,我们有Boost图形库

或者需要具有树状访问特性的容器为此我们有

  • std::map(和std::multimap)
  • std::set(和std::multiset)

基本上,这两个容器的特点是,它们实际上必须使用树来实现(尽管这实际上不是一个要求)。

另请参见此问题:C树实现


可能也是因为Boost中没有树容器。实现这样一个容器有很多方法,而且没有好的方法来满足每个使用它的人。

需要考虑的一些问题:-节点的子节点数是固定的还是可变的?-每个节点的开销是多少?-你需要父指针、兄弟指针等吗?-提供什么算法?-不同的迭代器、搜索算法等。

最后,问题是一个对每个人都足够有用的树容器太重了,不能满足大多数使用它的人。如果您正在寻找功能强大的东西,Boost图形库实际上是一个树库可以用来做什么的超集。

下面是一些其他通用树实现:-卡斯帕·皮特斯的树。-土坯森林-核心::树


STL的理念是,您选择一个基于保证的容器,而不是基于容器是如何实现的。例如,容器的选择可能基于快速查找的需要。对于您所关心的一切,容器可以实现为一个单向列表——只要搜索速度非常快,您就会很高兴。这是因为您无论如何都不会触及内部,而是使用迭代器或成员函数进行访问。您的代码与容器的实现方式无关,而是与它的速度、它是否具有固定的和定义的顺序、它在空间上是否有效等有关。


"I want to store a hierarchy of objects as a tree"

C++ 11已经来了又走了,他们仍然没有看到需要提供一个EDCOX1×0的概念,尽管这个想法确实出现了(见这里)。也许他们没有添加这一点的原因是,在现有容器之上构建自己的容器非常简单。例如。。。

1
2
3
4
5
6
template< typename T >
struct tree_node
   {
   T t;
   std::vector<tree_node> children;
   };

简单的遍历将使用递归…

1
2
3
4
5
6
template< typename T >
void tree_node<T>::walk_depth_first() const
   {
   cout<<t;
   for ( auto & n: children ) n.walk_depth_first();
   }

如果您希望维护一个层次结构,并且希望它与STL算法一起工作,那么事情可能会变得复杂。您可以构建自己的迭代器并实现一些兼容性,但是许多算法对于层次结构来说根本没有任何意义(例如,任何改变范围顺序的东西)。即使在层次结构中定义一个范围,也可能是一个混乱的业务。


如果您正在寻找一个RB树实现,那么stl_tree.h也可能适合您。


STD::地图是基于红黑树。您还可以使用其他容器来帮助实现自己的树类型。


在某种程度上,STD::MAP是一棵树(它需要具有与平衡二叉树相同的性能特性),但是它不会暴露其他树的功能。不包括真正的树数据结构的原因可能只是不包括STL中的所有内容。STL可以看作一个框架,用于实现您自己的算法和数据结构。

一般来说,如果您需要一个基本的库功能,而这不在STL中,那么修复方法就是查看boost。

否则,根据树的需要,会有很多库。


所有STL容器在外部用一个迭代机制表示为"序列"。树不遵循这个成语。


这一个看起来很有希望,似乎是你想要的:http://tree.phi-sci.com网站/


因为STL不是"一切"库。它基本上包含了建造东西所需的最小结构。


我认为没有STL树有几个原因。首先,树是递归数据结构的一种形式,就像容器(列表、向量、集合)一样,具有非常不同的精细结构,这使得正确的选择变得困难。它们也很容易使用STL以基本形式构造。

有限根树可以被认为是一个具有值或有效负载的容器,例如类A的一个实例和根(子)树的可能空集合;没有子树的树看起来像树叶。

1
2
3
4
5
6
7
8
9
10
11
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
{};

我们必须稍微考虑一下迭代器设计等,以及允许在树之间定义和高效地执行哪些产品和协同产品操作——原始STL必须编写得很好——这样,在默认情况下,空的集合、向量或列表容器实际上没有任何有效负载。

树在许多数学结构中扮演着重要的角色(参见《屠夫、格罗斯曼和拉森的经典论文》;也可以结合康纳斯和克里默的论文,以获取它们的示例,以及如何使用它们进行枚举)。认为他们的作用仅仅是促进某些其他行动是不正确的。相反,由于它们作为数据结构的基本角色,它们促进了这些任务。

但是,除了树之外,还有"共树";上面的树都具有这样的属性:如果删除根,则删除所有内容。

考虑到树上的迭代器,它们可能被实现为一个简单的迭代器堆栈,一个节点,以及它的父节点,…直到根。

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

但是,您可以拥有任意多个;它们共同构成一个"树",但是当所有箭头都朝着根方向流动时,这个协树可以通过迭代器迭代到普通迭代器和根;但是它不能跨或向下导航(其他迭代器不知道),也不能取消迭代器的集合。删除,除非跟踪所有实例。

树是非常有用的,它们有很多结构,这使得获得明确正确的方法成为一个严重的挑战。在我看来,这就是它们不在STL中实现的原因。此外,在过去,我看到人们信奉宗教,并且发现一种容器的概念,其中包含了自己类型的实例,这是一种挑战——但他们必须面对它——这就是树类型所代表的——它是一个节点,其中可能包含空的(较小的)树集合。当前语言允许它不受挑战地提供container的默认构造函数,不为B等分配堆上(或其他任何地方)的空间。

我个人会很高兴,如果这是一个很好的形式,找到它的方式进入标准。


在我看来,这是一个疏忽。但我认为有充分的理由不在STL中包含树结构。维护树有很多逻辑,最好将其作为成员函数写入基TreeNode对象中。当TreeNode被一个stl头包起来时,它就变得更乱了。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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); }
} ;


所有STL容器都可以与迭代器一起使用。你不能有一个迭代器和一个树,因为你没有"一个正确的"方法来遍历树。