September 27, 2009

tree.hh documentation
Kasper Peeters
kasper.peeters@gmail.com

Abstract The tree.hh library for C++ provides an STL-like container class for n-ary trees, templated over the data stored at the nodes. Various types of iterators are provided (post-order, pre-order, and others). Where possible the access methods are compatible with the STL or alternative algorithms are available. The library is available under the terms of the GNU General Public License. Code and examples available at: http://tree.phi-sci.com/ This documentation is not yet entirely complete. Refer to the tree.hh header ﬁle for a full list of member functions.

Many STL algorithms work on this data structure. In addition.hh library provides (at present) four diﬀerent iteration schemes.1
1. The elements at the top of the tree (here displayed at the left for convenience) are in the “head” (there can be more than one such element). The collection of these nodes is called the “head” of the tree. all nodes on a given level are doubly-linked using the “previous sibling” and “next sibling” links. To describe them. consider the following tree: root A B C D E F The three iteration types and the resulting order in which nodes are visited are tabulated below: 2
.
The tree class is templated over the data objects stored at the nodes. The tree. Every node is linked to its children using the “ﬁrst child” and “last child” links. there is a set of nodes which are characterised by the fact that they do not have any parents.
head first child node next sibling prev sibling node next sibling prev sibling last child node node first child node
node
Figure 1: Overview of the tree structure. and where necessary alternatives have been provided. It organises data in the form of a so-called n-ary tree.2
Iterators
The essential diﬀerence between a container with the structure of a tree and the STL containers is that the latter are “linear”. At the top of the tree. Nodes at the same level of the tree are called “siblings”. While the STL containers thus only have essentially one way in which one can iterate over their elements. this is not true for trees. while nodes that are below a given node are called its “children”.
1.1
Overview
The container class
The tree class of tree. See ﬁgure 1 for a pictorial illustration of this structure (90 degrees rotated for convenience). The “depth” of a given node refers to the horizontal distance from the head nodes.hh is a templated container class in the spirit of the STL. just like you can have a vector<string> you can now have a tree<string>. This is a tree in which every node is connected to an arbitrary number of child nodes.

Note that the value of the index is not checked and should therefore always be valid. in contrast to the “breadth-ﬁrst” iterator.and pre-order iterators are both also known as “depth-ﬁrst”.) A D (for ex. This is also how you insert the ﬁrst node into a tree. Inserting nodes Nodes can be inserted at the same depth as a given other node using the insert and insert after members functions. The post. Sibling iterators and ﬁxed-depth iterators iterate only over the nodes at a given depth of the tree. The parent of a given node can be reached by calling the parent member of the tree object.
3
. by using the iterator::begin() and iterator::end() member functions. If you want to (temporarily) make an iterator not go into the child subtree. One which takes a single node element as argument. it is also possible to use the operator+= and operator-= member functions to make more than one step at the same time (though these are linear time. you can get direct access to the nth child by using the child member function.e. The begin and end iterators of a tree can be obtained using begin() and end() (for pre-order iterators) or alternatively begin post() and end post() (for post-order iterators) and begin leaf() and end leaf() (for leaf iterators). is not an “end” iterator) can be tested using the is valid(iterator) member of the tree class. The other non-trivial constructor takes an iterator. while the latter iterates over all child nodes at the given depth. It constructs a tree with this node begin the sole node in the head (in other words. If you know the number of children of a given node. whether or not an iterator is actually pointing at a node (i. There are copy constructors that will convert iterators of the various types into each other.pre-order (default) post-order breadth-ﬁrst sibling ﬁxed-depth leaf
“element before children” “element after children” “only siblings”
pre order iterator post order iterator breadth first iterator sibling iterator fixed depth iterator leaf iterator
root A B C D E F B C A E F D root root A D B C E F (for ex. Tree traversal Besides the operator++ and operator-. not amortized constant). the begin and end sibling iterators can be obtained by calling begin(iterator) and end(iterator). Similarly. leaf iterators iterate over all leafs (bottom-most) nodes of the tree. Finally. The range of children of a given node can also be obtained directly from an iterator.
2
Basic operations
Initialising There are two nontrivial constructors. Appending child nodes Nodes can be added as children of a given node using the append child member function. call the member function skip children. and therefore also known under the name of iterator. and copies the subtree starting at that node into the newly created tree (useful for constructing new tree objects given by subtrees of existing trees). Finally.members for step-wise traversal through the tree.) A D BCEF
The pre-order ones are the default iterators. it is a combination of a trivial constructor together with a set head call). The former restrict themselves to the child nodes of one given node. The result of stepping beyond the end of the tree or stepping beyond the end of a sibling range (for sibling iterators) is undeﬁned. This will only keep eﬀect for a single increment or decrement of the iterator. giving it an iterator pointing to the node.

If you just want to erase the children. The iterator remains valid and remains pointing to the moved subtree. it does not know about the structure of the tree. BinaryPredicate) call of the tree class. As an addition to the STL algorithm. The reverse of this function (obtaining a sibling node given its index in the range of siblings) is called child(const iterator base&. move before and move after ones. and replaces it with the ‘source’ node and its children. iterator. use the index(sibling iterator) member function. source) removes the ‘target’ node and all its children. Determining depth The depth() member function returns the distance of a node to the root. it acts as apple banana pear strawberry cherry grape apple banana pear strawberry cherry grape
→
when the tree is ﬂattened at the “banana” node. More complicated move operations are the move ontop. Accessing siblings by their index See the next item. pointing to the (single-node) heads of two subtrees. The ‘source’ subtree is removed from its original location.1
Other algorithms
Non-mutating algorithms
Counting nodes The total number of nodes of a tree can be obtained using the size member function. use the erase children(iterator) call. while the number of children of a given node can be obtained with a call to number of children(iterator). use the erase(iterator) call.3
3. These all take two iterators. 4
. The member move ontop(target. Determining index in a sibling range In order to determine the index of a node in the range of siblings to which it belongs. use the equal(iterator. the number of nodes at a given depth (the number of siblings of a given node) can be obtained using the number of siblings member function. Similarly. The other two move members do a similar thing. iterator. diﬀering only in the node which is to be replaced.
3. If you want the comparison to take this into account. unsigned int). Moving or exchanging subtrees Simple exchange of one sibling node with the next one is done through the member function swap(sibling iterator).2
Mutating algorithms
Erasing nodes and subtrees In order to remove a node including its children from the tree. a source and a target. Replacing individual nodes or subtrees Flattening subtrees The procedure of moving all children of a given node to be siblings of that node is called “ﬂattening”. but not the node itself. the length of the ﬁrst range does not have to be equal to the length of the range pointed to by the second iterator. Comparing trees While the STL equal algorithm can be used to compare the values of the nodes in two diﬀerent trees. The ﬁrst sibling node has index 0. There is also an equal subtree algorithm which takes only two iterators.

template<class StrictWeakOrdering> void sort(sibling_iterator from. is to give the path that leads to the insertion point. given the tree apple banana pear strawberry cherry grape one could imagine using the sub-tree apple banana coconut raspberry to indicate that the nodes “coconut” and “raspberry” are to be inserted as new children of the “banana” node. If the nodes to which the two iterators point are not in the same sibling range (i. the result is undeﬁned. sibling_iterator to.hh this process is called tree merging. sibling_iterator to.
apple banana coconut raspberry tangerine plum blueberry orange
         
→
5
. It comes in two forms. not nodes. namely
void sort(sibling_iterator from. For instance. Sorting The standard STL sort algorithm is not very useful for trees. only node values get moved around (not their subtrees). There is also a slight variation of this member. Therefore.Extracting subtrees You can create a new tree object ﬁlled with the data of a subtree of the original tree. bool deep=false). If the boolean deep is true. In tree. This is analogous to the extraction of a substring of a string. StrictWeakOrdering comp. As in the STL. sibling iterator) which takes a range of siblings as argument. you can use the second form of this function to pass your own comparison class. bool deep=false).e. just like the STL sort. It can do the simple addition of children as above. Applying it to a tree would mean that the structure of the tree remains unmodiﬁed. Merging One way in which one might think of indicating the position where new nodes are to be inserted. which does not return a tree object but instead populates one that is passed as an argument (useful if you want to call this on a tree object subclassed from tree<T>. The relevant member function is subtree(sibling iterator.
The result of a call to either of these is that the nodes in the range described by the two iterators get sorted. because it only exchanges values. the tree class has its own sort member. the subtrees of all these nodes will get sorted as well (and so one can sort the entire tree in one call). not at the same depth in the tree). but actually handles the generic case too: as an example consider the merge apple banana pear strawberry cherry coconut raspberry grape tangerine plum blueberry orange
 apple  banana   pear  strawberry merge    cherry  grape  blueberry
.

6
. the arguments to merge are two sibling ranges.As is clear from the above.