为什么C ++ STL不提供任何“树”容器,而最好使用什么呢?
我想将对象层次结构存储为树,而不是使用树作为性能增强...
答案 0 :(得分:174)
您可能想要使用树的原因有两个:
您希望使用树状结构来镜像问题:
为此,我们有boost graph library
或者您想要一个具有树状访问特征的容器 为此,我们有
基本上这两个容器的特征是它们实际上必须使用树来实现(尽管这实际上不是必需的)。
另见这个问题: C tree Implementation
答案 1 :(得分:89)
可能出于同样的原因,在增强中没有树容器。有很多方法可以实现这样一个容器,并没有很好的方法来满足每个使用它的人。
需要考虑的一些问题:
- 节点的子节点数是固定的还是可变的?
- 每个节点有多少开销? - 即,你需要父指针,兄弟指针等
- 提供什么算法? - 不同的迭代器,搜索算法等。
最后,问题最终是一个对每个人都足够有用的树容器,对于大多数使用它的人来说太重了。如果您正在寻找功能强大的东西,Boost Graph Library本质上是树库可用的超集。
以下是一些其他通用树实现:
- Kasper Peeters' tree.hh
- Adobe's forest
- core::tree
答案 2 :(得分:50)
STL的理念是您选择基于保证的容器,而不是基于容器的实现方式。例如,您选择的容器可能基于快速查找的需要。对于你所关心的一切,容器可以实现为单向列表 - 只要搜索速度非常快,你就会感到高兴。那是因为你无论如何都没有触及内部,你正在使用迭代器或成员函数进行访问。您的代码不受容器实现方式的限制,而是绑定到它的速度,或者它是否具有固定和定义的顺序,或者它是否在空间上有效等等。
答案 3 :(得分:45)
“我想将对象的层次结构存储为树”
C ++ 11已经过去了,他们仍然认为不需要提供std::tree
,尽管这个想法确实出现了(参见here)。也许他们没有添加这个的原因是,在现有容器之上构建自己的容易起来非常容易。例如......
template< typename T >
struct tree_node
{
T t;
std::vector<tree_node> children;
};
一个简单的遍历将使用递归...
template< typename T >
void tree_node<T>::walk_depth_first() const
{
cout<<t;
for ( auto & n: children ) n.walk_depth_first();
}
如果您想维护层次结构和,您希望它与STL algorithms一起使用,那么事情可能会变得复杂。您可以构建自己的迭代器并实现一些兼容性,但是许多算法对层次结构没有任何意义(例如,任何改变范围顺序的东西)。即使在层次结构中定义范围也可能是一个混乱的业务。
答案 4 :(得分:42)
如果您正在寻找RB树实施,那么stl_tree.h也可能适合您。
答案 5 :(得分:12)
std :: map基于red black tree。您还可以使用其他containers来帮助您实现自己的树类型。
答案 6 :(得分:8)
在某种程度上,std :: map是一棵树(它需要具有与平衡二叉树相同的性能特征),但它不会暴露其他树功能。不包括真实树数据结构的可能原因可能只是不包括stl中的所有内容。可以将stl看作是用于实现自己的算法和数据结构的框架。
一般来说,如果你想要一个基本的库功能,那就不在stl中,修复就是看BOOST。
答案 7 :(得分:6)
所有STL容器外部表示为具有一种迭代机制的“序列”。 树木不遵循这个习语。
答案 8 :(得分:4)
因为STL不是“一切”库。它基本上包含构建东西所需的最小结构。
答案 9 :(得分:4)
这个看起来很有前途,似乎是你正在寻找的: http://tree.phi-sci.com/
答案 10 :(得分:2)
TreeNode
对象。当TreeNode
被包裹在STL标题中时,它会变得更加混乱。
例如:
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); }
} ;
答案 11 :(得分:2)
我认为没有stl树有几个原因。主要树是递归数据结构的一种形式,它像容器(列表,向量,集)一样,具有非常不同的精细结构,这使得正确的选择变得棘手。使用STL,它们也很容易以基本形式构建。
可以将有限的有根树视为具有值或有效负载的容器,例如A类的实例,以及可能为空的根(子)树集合;没有子树的树虽然是叶子。
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必须写得很好 - 以便空集,矢量或列表容器在默认情况下实际上没有任何有效负载。
树木在许多数学结构中起着至关重要的作用(参见Butcher,Grossman和Larsen的经典论文;还有Connes和Kriemer的论文,例如它们可以加入,以及它们如何被用来枚举)。认为他们的角色只是为了促进某些其他操作是不正确的。相反,由于它们作为数据结构的基本作用,它们促进了这些任务。
然而,除了树木,还有“共同树”;上面的树都有一个属性,如果你删除了根,你就删除了所有内容。
考虑树上的迭代器,可能它们将被实现为一个简单的迭代器堆栈,一个节点,它的父节点,......直到根节点。
template<class TREE>
struct node_iterator : std::stack<TREE::iterator>{
operator*() {return *back();}
...};
然而,你可以拥有任意多的人;它们共同构成了一个“树”,但是所有箭头都朝着根方向流动,这个共同树可以通过迭代器迭代到平凡的迭代器和根;但是它不能跨越或向下导航(其他迭代器不为它所知),也不能删除迭代器的集合,除非跟踪所有实例。
树木非常有用,它们有很多结构,这使得获得明确正确的方法成为一个严峻的挑战。在我看来,这就是为什么它们没有在STL中实现的原因。此外,在过去,我看到人们变得宗教,并且发现包含自己类型的实例的容器类型的想法具有挑战性 - 但是他们必须面对它 - 这就是树类型所代表的 - 它是一个包含可能是(较小的)树木的空集合。当前语言允许它没有挑战,提供container<B>
的默认构造函数不会为B
等在堆上(或其他任何地方)分配空间。
如果这样做,我会很高兴以良好的形式进入标准。
答案 12 :(得分:0)
通读这里的答案,常见的命名原因是:一个人不能遍历该树,或者该树不具有与其他STL容器类似的接口,并且不能使用具有这种树结构的STL算法。
考虑到这一点,我尝试设计自己的树数据结构,该结构将提供类似STL的接口,并尽可能地与现有STL算法一起使用。
我的想法是,树必须基于现有的STL容器,并且不能隐藏该容器,以便可以与STL算法一起使用。
树必须提供的另一个重要功能是遍历迭代器。
这就是我想出的:https://github.com/cppfw/utki/blob/master/src/utki/tree.hpp
以下是测试:https://github.com/cppfw/utki/blob/master/tests/tree/tests.cpp
答案 13 :(得分:-8)
所有STL容器都可以与迭代器一起使用。你不能让迭代器成为一棵树,因为你没有“一个正确”的方式来穿过这棵树。