trees
Quickstart
Impatient readers can start with the notation chapter.
Overview
-
Step-by-step creating, reading, updating, deleting and iterating nodes with assocated data items.
-
Compact notations to express trees:
-
,/
encoded or tuple encoded trees. -
Depth first search cursor.
-
Breadth first search iterators.
-
Trees can be built by stages, with nodes stored scatteredly among memory.
-
Trees can be built once through, with nodes stored contiguously.
-
Support exclusive ownership with static borrow check.
-
Support shared ownership with dynamic borrow check.
Concepts
The definitions of trees are as follows:
-
A tree is composed of one or more nodes.
-
A node has zero or more nodes as its children.
-
A node has zero or one node as its parent.
-
The node without parent is called the tree's root.
-
A node associates data of generic type, and data of all nodes in the same tree must be of the same type.
-
Children nodes preserve insertion order.
-
A forest is composed of zero or more nodes.
-
A node's children is a non-owning forest.
-
Two node's are siblings if their parents are the same node.
Tree
Tree
is composed of one or more Node
s. A tree is always non-empty.
Tree root
Tree
can be constructed from a root value of type T
, which can be accessed
later via root()
and root_mut()
.
Children
Tree
can insert/delete child Node
s at front/back of its children list,
which is a conceptual Forest
and can be removed once the abandon()
is called.
Degree
The amount of child nodes of a tree is called tree's degree()
.
The amount of all nodes of a tree is returned by node_count()
.
Breadth first search
A Tree
may be converted to an owning iterator via into_bfs()
, which
iterates all its nodes in the manner of breadth first search.
Forest
A Forest
is composed of zero or more Node
s. An empty forest is constructed
via Forest::new()
.
Children
A Forest
can be considered as a list of Node
s, with the ability of
inserting/deleting child nodes at its front/back: push_front()
, push_back()
,
pop_front()
and pop_back()
. The first and last child can be accessed via
front()
/front_mut()
and back()
/back_mut()
.
A forest can merge another one via prepend()
or append()
.
Forest's children can be iterated via iter()
/iter_mut()
/into_iter()
.
Degree
The amount of child nodes of a forest is called forest's degree()
.
A forest has_no_child()
if its degree is 0.
The amount of desendant nodes of a forest is returned by node_count()
.
Breadth first search
A Forest
may provide bfs()
/bfs_mut()
/into_bfs()
, which iterate all
its child nodes in the manner of breadth first search.
Node
A Node
is associated with data of type T
, which is accessible via
data()
/data_mut()
or even pub data
field.
Node may have zero or more Node
s as its children; zero or one node as its
parent()
.
Node can be cloned to an owning Tree
via to_tree()
, with all its descendant
nodes cloned.
Node can be detach()
-ed from its parent, making a standalone Tree
.
Children
The children of a Node
can be considered as a list of Node
s, with the
ability of inserting/deleting child nodes at its front/back: push_front()
,
push_back()
, pop_front()
and pop_back()
. The first and last child can be
accessed via front()
/front_mut()
and back()
/back_mut()
.
A forest can be merged via prepend()
or append()
.
Node's children can be iterated via iter()
/iter_mut()
/into_iter()
.
Siblings
Two node's are siblings if their parents are the same node.
Node can add an sibling node before/after itself via insert_prev_sib()
/
insert_next_sib()
.
Degree
The amount of child nodes of a node is called node's degree()
.
A node has_no_child()
if its degree is 0.
The amount of all nodes( including itself and its descendants ) of a node is
returned by node_count()
.
Breadth first search
A Node
may provide bfs()
/bfs_mut()
, which iterate all its child nodes in
the manner of breadth first search.
Create, read, update, delete
Tree CRUD APIs
A tree can be created via Tree::new()
which constructs the root node only with
associated data.
#![allow(unused)] fn main() { use trees::Tree; let mut tree = Tree::new(9); }
This will construct a tree composed of only one root node with data 9.
.............
. 9 .
.............
The root node can be accessed via Tree::root()
/Tree::root_mut()
.
#![allow(unused)] fn main() { let root = tree.root(); assert!( root.has_no_child() ); }
The associated data can be read/updated via Tree::data()
/Tree::data_mut()
.
#![allow(unused)] fn main() { assert_eq!( root.data(), &9 ); let root = tree.root_mut(); *root.data_mut() = 0; }
The root data has been modified:
.............
. 0 .
.............
A tree can adopt other trees as children via Tree::push_back()
.
#![allow(unused)] fn main() { tree.push_back( Tree::new(1) ); tree.push_back( Tree::new(2) ); }
This will add two child nodes.
.............
. 0 .
. / \ .
. 1 2 .
.............
The child nodes can be accessed via iter()
.
#![allow(unused)] fn main() { let iter = tree.iter(); assert_eq!( iter.next().unwrap().data(), &1 ); assert_eq!( iter.next().unwrap().data(), &2 ); }
Specially, the first/last child can be accessed via front()
/back()
.
#![allow(unused)] fn main() { assert_eq!( tree.front().unwrap().data(), &1 ); assert_eq!( tree.back() .unwrap().data(), &2 ); }
A node can adopt other trees as children via Node::push_back()
.
#![allow(unused)] fn main() { let node_1 = tree.front_mut().unwrap(); node_1.push_back( Tree::new(3) ); node_1.push_back( Tree::new(4) ); node_1.push_back( Tree::new(5) ); }
The tree will be:
.............
. 0 .
. / \ .
. 1 2 .
. /|\ .
.3 4 5 .
.............
Nodes can be removed via Node::detach()
.
#![allow(unused)] fn main() { let tree_4 = node_1.iter_mut().nth(1).unwrap().detach(); }
The tree will be:
.............
. 0 .
. / \ .
. 1 2 .
. / \ .
.3 5 .
.............
Specially, the first/last child can be removed via pop_front()
/pop_back()
.
#![allow(unused)] fn main() { node_1.pop_front(); }
The tree will be:
.............
. 0 .
. / \ .
. 1 2 .
. | .
. 5 .
.............
Forest CRUD APIs
The Forest
's APIs are similar with Tree
's. The main difference is that a
Forest
does not have root data, and may be empty.
Node CRUD APIs
The Node
's APIs are similar with Tree
's. The main difference is that Node
s
provided to library users are always in the form of &Node<_>
or
Pin<&mut Node<_>>
, which do not have ownership.
Notations
Using push_back()
to construct a big tree can be verbose. This library
provides some convenient notations to express trees compactly.
Operator overloading of -
, /
for scatteredly stored trees
Tree notations
#![allow(unused)] fn main() { use trees::tr; // tr stands for tree tr(0); // a single tree node with data 0. tr(0) has no children tr(0) /tr(1); // tr(0) with one child tr(1) tr(0) /tr(1)/tr(2); // tr(0) with children tr(1) and tr(2) // tr(0) with children tr(1) and tr(4), // tr(1) with children tr(2) and tr(3), // tr(4) with children tr(5) and tr(6). // Spaces and line breaks are for pretty format only. tr(0) /( tr(1) /tr(2)/tr(3) ) /( tr(4) /tr(5)/tr(6) ); }
Forest notations
#![allow(unused)] fn main() { use trees::{fr, tr}; // fr stands for forest fr::<i32>(); // an empty forest fr() - tr(1); // forest with one child tr(1) - tr(1); // same forest as above, the leading `fr()` can be omitted. - tr(1) - tr(2); // forest with child tr(1) and tr(2). tr(1) - tr(2); // same forest as above, the leading `-` can be omitted. // forest with children tr(1) and tr(4), // tr(1) with children tr(2) and tr(3), // tr(4) with children tr(5) and tr(6). -( tr(1) /tr(2)/tr(3) ) -( tr(4) /tr(5)/tr(6) ); // A tree tr(0) whose children are the forest described above. tr(0) /( -( tr(1) /( -tr(2)-tr(3) ) ) -( tr(4) /( -tr(5)-tr(6) ) ) ); }
Tuples for contiguously stored trees
Tree notations
#![allow(unused)] fn main() { let tree = trees::Tree::<i32>::from_tuple(( 0, (1,2,3), (4,5,6) )); }
The constructed tree is equvalent to:
#![allow(unused)] fn main() { tr(0) /( -( tr(1) /( -tr(2)-tr(3) ) ) -( tr(4) /( -tr(5)-tr(6) ) ) ); }
Forest notations
#![allow(unused)] fn main() { let forest = trees::Forest::<i32>::from_tuple(( (1,2,3), (4,5,6) )); }
The constructed forest is equivalent to:
#![allow(unused)] fn main() { -( tr(1) /( -tr(2)-tr(3) ) ) -( tr(4) /( -tr(5)-tr(6) ) ); }
String representation
In current implementation of Display
, children are separated by spaces and
grouped in the parentheses that follow their parent closely.
Example
.............
. 0 .
. / \ .
. 1 4 .
. / \ / \ .
.2 3 5 6.
.............
String representation of the tree drawn above is:
0( 1( 2 3 ) 4( 5 6 ) )
#![allow(unused)] fn main() { use trees::{tr, fr}; let tree = tr(0) /( tr(1) /tr(2)/tr(3) ) /( tr(4) /tr(5)/tr(6) ); let str_repr = "0( 1( 2 3 ) 4( 5 6 ) )"; assert_eq!( tree.to_string(), str_repr ); assert_eq!( fr::<i32>().to_string(), "()" ); let forest = -( tr(1) /tr(2)/tr(3) ) -( tr(4) /tr(5)/tr(6) ); let str_repr = "( 1( 2 3 ) 4( 5 6 ) )"; assert_eq!( forest.to_string(), str_repr ); }
Traversal via iterators
Node provides iterators iter()
/iter_mut()
to iterate over its child nodes,
each of which provides iterators to iterate over their child nodes, and so on.
Example
#![allow(unused)] fn main() { use trees::{tr, Node}; use std::fmt::Display; let tree = tr(0) /( tr(1) /tr(2)/tr(3) ) /( tr(4) /tr(5)/tr(6) ); fn tree_to_string<T:Display>( node: &Node<T> ) -> String { if node.has_no_child() { node.data.to_string() } else { format!( "{}( {})", node.data, node.iter().fold( String::new(), |s,c| format!( "{}{} ", s, tree_to_string(c) )) } } assert_eq!( tree_to_string( &tree ), "0( 1( 2 3 ) 4( 5 6 ) )" ); }
Depth first traversal
Tree<T>
/Forest<T>
can be converted to TreeWalk<T>
/ForestWalk<T>
which
act like cursors for depth first traversal.
Keeping on get()
-ing current node then forward()
ing the cursor results in a
depth first search sequence. The two operations are combined as next()
.
During traversal, the cursor can jump to_parent()
/to_child()
/to_sib()
at
any time. To restart DFS search of a node, use revisit()
.
Example
#![allow(unused)] fn main() { use trees::{tr, Visit, TreeWalk}; let tree = tr(0) /( tr(1)/tr(2)/tr(3) ) /( tr(4)/tr(5)/tr(6) ); let mut walk = TreeWalk::from( tree ); assert_eq!( walk.get(), Some( Visit::Begin( ( tr(0) /( tr(1)/tr(2)/tr(3) ) /( tr(4)/tr(5)/tr(6) ) ) .root() ))); walk.forward(); assert_eq!( walk.get(), Some( Visit::Begin( ( tr(1) /tr(2)/tr(3) ) .root() ))); walk.forward(); assert_eq!( walk.get(), Some( Visit::Leaf( tr(2) .root() ))); walk.forward(); assert_eq!( walk.get(), Some( Visit::Leaf( tr(3) .root() ))); walk.forward(); assert_eq!( walk.get(), Some( Visit::End( ( tr(1) /tr(2)/tr(3) ) .root() ))); walk.forward(); assert_eq!( walk.get(), Some( Visit::Begin( ( tr(4) /tr(5)/tr(6) ) .root() ))); walk.forward(); assert_eq!( walk.get(), Some( Visit::Leaf( tr(5) .root() ))); walk.forward(); assert_eq!( walk.get(), Some( Visit::Leaf( tr(6) .root() ))); walk.forward(); assert_eq!( walk.get(), Some( Visit::End( ( tr(4) /tr(5)/tr(6) ) .root() ))); walk.forward(); assert_eq!( walk.get(), Some( Visit::End( ( tr(0) /( tr(1)/tr(2)/tr(3) ) /( tr(4)/tr(5)/tr(6) ) ) .root() ))); walk.forward(); assert_eq!( walk.get(), None ); walk.forward(); assert_eq!( walk.get(), None ); }
Breath first traversal
Tree
/Forest
/Node
may provide bfs()
/bfs_mut()
/into_bfs()
,
which iterate all its child nodes in the manner of breadth first search. Note
that not all data structures provide all three kinds of iterators.
During breadth first search, you can get:
-
data associated with
Node
. -
size info -- degree and node count.
Example of BFS iteration
#![allow(unused)] fn main() { use trees::{Size, bfs, tr}; let tree = tr(0) /( tr(1)/tr(2)/tr(3) ) /( tr(4)/tr(5)/tr(6) ); let visits = tree.into_bfs().iter.collect::<Vec<_>>(); assert_eq!( visits, vec![ bfs::Visit{ data: 0, size: Size{ degree: 2, descendants: 6 }}, bfs::Visit{ data: 1, size: Size{ degree: 2, descendants: 2 }}, bfs::Visit{ data: 4, size: Size{ degree: 2, descendants: 2 }}, bfs::Visit{ data: 2, size: Size{ degree: 0, descendants: 0 }}, bfs::Visit{ data: 3, size: Size{ degree: 0, descendants: 0 }}, bfs::Visit{ data: 5, size: Size{ degree: 0, descendants: 0 }}, bfs::Visit{ data: 6, size: Size{ degree: 0, descendants: 0 }}, ]); }
Trees can be constructed from and converted into an owning BFS iterator, making it a bridge for conversions between trees.
Example of collecting scattered nodes to piled ones
#![allow(unused)] fn main() { use trees::{Tree, tr}; let scattered_tree = tr(0) /( tr(1)/tr(2)/tr(3) ) /( tr(4)/tr(5)/tr(6) ); let piled_tree = Tree::<i32>::from( scattered_tree.into_bfs() ); }
Shared ownership
Tree
s, Forest
s have exclusive ownership and borrowing of their Node
s are
statically checked.
A Tree
could be safely converted to RcNode
to support shared ownership and
dynamic borrow check.
#![allow(unused)] fn main() { use trees::{RcNode, Tree}; let tree = Tree::<i32>::from(( 0, (1,2,3), (4,5,6), )); let root = RcNode::from( tree ); }
An RcNode
could be unsafely converted to Tree
to get exclusive ownership and
static borrow check.
#![allow(unused)] fn main() { let tree = unsafe{ root.into_tree() }; }
WeakNode
is the non-owning version of RcNode
.