summaryrefslogtreecommitdiffstats
path: root/doc/flat_segment_tree.rst
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--doc/flat_segment_tree.rst268
1 files changed, 268 insertions, 0 deletions
diff --git a/doc/flat_segment_tree.rst b/doc/flat_segment_tree.rst
new file mode 100644
index 0000000..d5e7b8a
--- /dev/null
+++ b/doc/flat_segment_tree.rst
@@ -0,0 +1,268 @@
+.. highlight:: cpp
+
+Flat Segment Tree
+=================
+
+Overview
+--------
+
+Flat segment tree is a derivative of `segment tree
+<https://en.wikipedia.org/wiki/Segment_tree>`_, and is designed to store
+non-overlapping 1-dimensional range values such that *the values of the
+neighboring ranges are guaranteed to be different.* An insertion of a range
+value into this structure will always overwrite one or more existing ranges
+that overlap with the new range. If an insertion of a new range would cause
+any adjacent ranges to have the equal value, those ranges will be merged into
+one range.
+
+An instance of this structure is initialized with fixed lower and upper
+bounaries, which will not change throughout the life time of the instance.
+
+The flat segment tree structure consists of two parts: the leaf-node part
+which also forms a doubly-linked list, and the non-leaf-node part which forms
+a balanced-binary tree and is used only when performing tree-based queries.
+The range values are stored in the leaf-nodes, while the non-leaf nodes are
+used only for queries.
+
+
+Quick start
+-----------
+
+This section demonstrates a simple use case of storing non-overlapping ranged
+values and performing queries using :cpp:class:`~mdds::flat_segment_tree`.
+
+First, we need to instantiate a concrete type from the template:
+
+.. literalinclude:: ../example/flat_segment_tree.cpp
+ :language: C++
+ :start-after: //!code-start: type
+ :end-before: //!code-end: type
+ :dedent: 4
+
+then create an instance of this type:
+
+.. literalinclude:: ../example/flat_segment_tree.cpp
+ :language: C++
+ :start-after: //!code-start: instance
+ :end-before: //!code-end: instance
+ :dedent: 4
+
+Here, the first and second arguments specify the lower and upper boundaries of
+the whole segment. The third argument specifies the value for the empty
+segments. What this line does is to create a new instance and initializes it
+with one initial segment ranging from 0 to 500 with a value of 0:
+
+.. figure:: _static/images/fst-example1-initial.svg
+ :align: center
+
+ The new instance is initialized with an initial segment.
+
+Internally, this initial range is represented by two leaf nodes, with the
+first one storing the start key and the value for the segment both of which
+happen to be 0 in this example, and the second one storing the end key of 500.
+Note that the end key of a segment is not inclusive.
+
+The following lines insert two new segments into this structure:
+
+.. literalinclude:: ../example/flat_segment_tree.cpp
+ :language: C++
+ :start-after: //!code-start: insert-1
+ :end-before: //!code-end: insert-1
+ :dedent: 4
+
+The first line inserts a segment ranging from 10 to 20 with a value of 10, and
+the second line from 50 to 70 with a value of 15:
+
+.. figure:: _static/images/fst-example1-insert1.svg
+ :align: center
+
+ Two new segments have been inserted.
+
+You can insert a new segment either via :cpp:func:`~mdds::flat_segment_tree::insert_front`
+or :cpp:func:`~mdds::flat_segment_tree::insert_back`. The end result will be
+the same regardless of which method you use; the difference is that
+:cpp:func:`~mdds::flat_segment_tree::insert_front` begins its search for
+the insertion point from the first node associated with the minimum key value,
+whereas :cpp:func:`~mdds::flat_segment_tree::insert_back` starts its search
+from the last node associated with the maximum key value.
+
+After the insertions, the tree now contains a total of six leaf nodes to
+represent all stored segments. Note that one leaf node typically represents
+both the end of a segment and the start of the adjacent segment that comes
+after it, unless it's either the first or the last node.
+
+The next line inserts another segment ranging from 60 to 65 having a value of
+5:
+
+.. literalinclude:: ../example/flat_segment_tree.cpp
+ :language: C++
+ :start-after: //!code-start: insert-2
+ :end-before: //!code-end: insert-2
+ :dedent: 4
+
+As this new segment overlaps with the existing segment of 50 to 70, it will
+cut into a middle part of that segment to make room for the new segment. At
+this point, the tree contains a total of eight leaf nodes representing seven
+segments:
+
+.. figure:: _static/images/fst-example1-insert2.svg
+ :align: center
+
+ A new segment has been inserted that overlaps an existing non-empty segment.
+
+Next, we are going to query the value associated with a key of 15 via
+:cpp:func:`~mdds::flat_segment_tree::search`:
+
+.. literalinclude:: ../example/flat_segment_tree.cpp
+ :language: C++
+ :start-after: //!code-start: linear-search
+ :end-before: //!code-end: linear-search
+ :dedent: 4
+
+Executing this code will yield the following output:
+
+.. code-block:: none
+
+ The value at 15 is 10, and this segment spans from 10 to 20
+
+One thing to note is that the :cpp:func:`~mdds::flat_segment_tree::search`
+method performs a linear search which involves traversing only through the
+leaf nodes in this data structure in order to find the target segment. As
+such, the worst-case lookup performance is directly proportional to the number
+of leaf nodes.
+
+There is another way to perform the query with better worse-case performance,
+that is through :cpp:func:`~mdds::flat_segment_tree::search_tree` as seen in
+the following code:
+
+.. literalinclude:: ../example/flat_segment_tree.cpp
+ :language: C++
+ :start-after: //!code-start: tree-search
+ :end-before: //!code-end: tree-search
+ :dedent: 4
+
+The signature of the :cpp:func:`~mdds::flat_segment_tree::search_tree` method
+is identical to that of the :cpp:func:`~mdds::flat_segment_tree::search` method
+except for the name. This code generates the following output:
+
+.. code-block:: none
+
+ The value at 62 is 5, and this segment spans from 60 to 65
+
+Query via :cpp:func:`~mdds::flat_segment_tree::search_tree` generally performs
+better since it traverses through the search tree to find the target segment.
+But it does require the search tree to be built ahead of time by calling
+:cpp:func:`~mdds::flat_segment_tree::build_tree`. Please be aware that if the
+segments have been modified after the tree was last built, you will have to rebuild
+the tree by calling :cpp:func:`~mdds::flat_segment_tree::build_tree`.
+
+.. warning::
+
+ You need to build the tree by calling :cpp:func:`~mdds::flat_segment_tree::build_tree`
+ before performing a tree-based search via :cpp:func:`~mdds::flat_segment_tree::search_tree`.
+ If the segments have been modified after the tree was last built, you will have to
+ rebuild the tree by calling :cpp:func:`~mdds::flat_segment_tree::build_tree` again.
+
+
+Iterate through stored segments
+-------------------------------
+
+:cpp:class:`~mdds::flat_segment_tree` supports two types of iterators to allow
+you to iterate through the segments stored in your tree. The first way is to
+iterate through the individual leaf nodes one at a time by using
+:cpp:func:`~mdds::flat_segment_tree::begin` and :cpp:func:`~mdds::flat_segment_tree::end`:
+
+.. literalinclude:: ../example/flat_segment_tree_itrs.cpp
+ :language: C++
+ :start-after: //!code-start: iterate-nodes
+ :end-before: //!code-end: iterate-nodes
+ :dedent: 4
+
+Each iterator value contains a pair of two values named ``first`` and ``second``,
+with the first one being the key of the segment that the node initiates, and the
+second one being the value associated with that segment. When executing this
+code with the tree from the example code above, you'll get the following output:
+
+.. code-block:: none
+
+ key: 0; value: 0
+ key: 10; value: 10
+ key: 20; value: 0
+ key: 50; value: 15
+ key: 60; value: 5
+ key: 65; value: 15
+ key: 70; value: 0
+ key: 500; value: 0
+
+Each node stores the start key and the value of the segment it initiates, and
+the key stored in each node is also the end key of the segment that the
+previous node initiates except for the first node. Note that the value stored
+in the last node is currently not used. It is set to be the zero value of the
+value type, but this may change in the future.
+
+One thing to keep in mind is that :cpp:class:`~mdds::flat_segment_tree` does
+not support mutable iterators that let you modify the stored keys or values.
+
+.. note::
+
+ :cpp:class:`~mdds::flat_segment_tree` does not support mutable iterators;
+ you can only traverse the values in a read-only fashion.
+
+You can also use range-based for loop to iterate through the leaf nodes in a
+similar fashion:
+
+.. literalinclude:: ../example/flat_segment_tree_itrs.cpp
+ :language: C++
+ :start-after: //!code-start: loop-nodes
+ :end-before: //!code-end: loop-nodes
+ :dedent: 4
+
+The output from this code is identical to that from the previous one.
+
+Now, one major inconvenience of navigating through the individual leaf nodes
+is that you need to manually keep track of the start and end points of each
+segment if you need to operate on the segments rather than the nodes that
+comprise the segments. The good news is that
+:cpp:class:`~mdds::flat_segment_tree` does provide a way to iterate through
+the segments directly as the following code demonstrates:
+
+.. literalinclude:: ../example/flat_segment_tree_itrs.cpp
+ :language: C++
+ :start-after: //!code-start: iterate-segments
+ :end-before: //!code-end: iterate-segments
+ :dedent: 4
+
+This code uses :cpp:func:`~mdds::flat_segment_tree::begin_segment` and
+:cpp:func:`~mdds::flat_segment_tree::end_segment` to iterate through one
+segment at a time with the value of each iterator containing ``start``,
+``end`` and ``value`` members that correspond with the start key, end key and
+the value of the segment, respectively. Running this code produces the
+following output:
+
+.. code-block:: none
+
+ start: 0; end: 10; value: 0
+ start: 10; end: 20; value: 10
+ start: 20; end: 50; value: 0
+ start: 50; end: 60; value: 15
+ start: 60; end: 65; value: 5
+ start: 65; end: 70; value: 15
+ start: 70; end: 500; value: 0
+
+It's also possible to iterate through the segments in a range-based for loop, by
+calling :cpp:func:`~mdds::flat_segment_tree::segment_range()`:
+
+.. literalinclude:: ../example/flat_segment_tree_itrs.cpp
+ :language: C++
+ :start-after: //!code-start: loop-segments
+ :end-before: //!code-end: loop-segments
+ :dedent: 4
+
+This code should generate output identical to that of the previous code.
+
+
+API Reference
+-------------
+
+.. doxygenclass:: mdds::flat_segment_tree
+ :members: