...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
The name treap is a mixture of tree and heap indicating that Treaps exhibit the properties of both binary search trees and heaps. A treap is a binary search tree that orders the nodes by a key but also by a priority attribute. The nodes are ordered so that the keys form a binary search tree and the priorities obey the max heap order property.
If priorities are non-random, the tree will usually be unbalanced; this worse theoretical average-case behavior may be outweighed by better expected-case behavior, as the most important items will be near the root. This means most important objects will be retrieved faster than less important items and for items keys with equal keys most important objects will be found first. These properties are important for some applications.
The priority comparison will be provided just like the key comparison, via a function object that will be stored in the intrusive container. This means that the priority can be stored in the value to be introduced in the treap or computed on flight (via hashing or similar).
Boost.Intrusive offers 3 containers based
on treaps: treap_set
,
treap_multiset
and treap
. The first two
are similar to set
or multiset
and the latter is a generalization
that offers functions both to insert unique and multiple keys.
The memory overhead of these containers with Boost.Intrusive hooks is 3 pointers.
An empty, treap_set
,
treap_multiset
or treap
has also the
size of 3 pointers and an integer (supposing empty function objects for key
and priority comparison and constant-time size).
treap_set
, treap_multiset
and treap
don't use their own hooks
but plain binary search tree hooks. This has many advantages since binary
search tree hooks can also be used to insert values in splay containers and
scapegoat trees.
template <class ...Options> class bs_set_base_hook;
bs_set_base_hook
:
the user class derives publicly from this class to make it compatible
with scapegoat tree based containers.
template <class ...Options> class bs_set_member_hook;
set_member_hook
:
the user class contains a public member of this class to make it compatible
with scapegoat tree based containers.
bs_set_base_hook
and bs_set_member_hook
receive the same options explained in the section How
to use Boost.Intrusive:
tag<class Tag>
(for base hooks only): This argument serves as a tag, so you can derive
from more than one base hook. Default: tag<default_tag>
.
link_mode<link_mode_type
LinkMode>
:
The linking policy. Default: link_mode<safe_link>
.
void_pointer<class VoidPointer>
:
The pointer type to be used internally in the hook and propagated to
the container. Default: void_pointer<void*>
.
template <class T, class ...Options> class treap_set; template <class T, class ...Options> class treap_multiset; template <class T, class ...Options> class treap;
These containers receive the same options explained in the section How to use Boost.Intrusive:
base_hook<class Hook>
/ member_hook<class T, class Hook, Hook T::* PtrToMember>
/ value_traits<class ValueTraits>
:
To specify the hook type or value traits used to configure the container.
(To learn about value traits go to the section Containers
with custom ValueTraits.)
constant_time_size<bool Enabled>
:
To activate the constant-time size()
operation. Default: constant_time_size<true>
size_type<bool Enabled>
:
To specify the type that will be used to store the size of the container.
Default: size_type<std::size_t>
And they also can receive additional options:
compare<class Compare>
:
Comparison function for the objects to be inserted in containers. The
comparison functor must induce a strict weak ordering. Default: compare<
std::less<T> >
priority<class PriorityCompare>
:
Priority Comparison function for the objects to be inserted in containers.
The comparison functor must induce a strict weak ordering. Default:
priority<
priority_compare<T> >
The default priority_compare<T>
object function will call an unqualified function priority_order
passing two constant T
references
as arguments and should return true if the first argument has higher priority
(it will be searched faster), inducing strict weak ordering. The function
will be found using ADL lookup so that the user just needs to define a priority_order
function in the same namespace
as his class:
struct MyType { friend bool priority_order(const MyType &a, const MyType &b) {...} };
or
namespace mytype { struct MyType{ ... }; bool priority_order(const MyType &a, const MyType &b) {...} } //namespace mytype {
In general, intrusive containers offer strong safety guarantees, but treap
containers must deal with two possibly throwing functors (one for value ordering,
another for priority ordering). Moreover, treap erasure operations require
rotations based on the priority order function and this issue degrades usual
erase(const_iterator)
no-throw guarantee. However, intrusive offers the strongest possible behaviour
in these situations. In summary:
Now let's see a small example using both hooks and treap_set
/
treap_multiset
containers:
#include <boost/intrusive/treap_set.hpp> #include <vector> #include <algorithm> #include <cassert> using namespace boost::intrusive; class MyClass : public bs_set_base_hook<> //This is a base hook { int int_; unsigned int prio_; public: //This is a member hook bs_set_member_hook<> member_hook_; MyClass(int i, unsigned int prio) : int_(i), prio_(prio) {} unsigned int get_priority() const { return this->prio_; } //Less and greater operators friend bool operator< (const MyClass &a, const MyClass &b) { return a.int_ < b.int_; } friend bool operator> (const MyClass &a, const MyClass &b) { return a.int_ > b.int_; } //Default priority compare friend bool priority_order (const MyClass &a, const MyClass &b) { return a.prio_ < b.prio_; } //Lower value means higher priority //Inverse priority compare friend bool priority_inverse_order (const MyClass &a, const MyClass &b) { return a.prio_ > b.prio_; } //Higher value means higher priority }; struct inverse_priority { bool operator()(const MyClass &a, const MyClass &b) const { return priority_inverse_order(a, b); } }; //Define an treap_set using the base hook that will store values in reverse order typedef treap_set< MyClass, compare<std::greater<MyClass> > > BaseSet; //Define an multiset using the member hook that will store typedef member_hook<MyClass, bs_set_member_hook<>, &MyClass::member_hook_> MemberOption; typedef treap_multiset < MyClass, MemberOption, priority<inverse_priority> > MemberMultiset; int main() { typedef std::vector<MyClass>::iterator VectIt; //Create several MyClass objects, each one with a different value std::vector<MyClass> values; for(int i = 0; i < 100; ++i) values.push_back(MyClass(i, (i % 10))); BaseSet baseset; MemberMultiset membermultiset; //Now insert them in the sets for(VectIt it(values.begin()), itend(values.end()); it != itend; ++it){ baseset.insert(*it); membermultiset.insert(*it); } //Now test treap_sets { BaseSet::reverse_iterator rbit(baseset.rbegin()); MemberMultiset::iterator mit(membermultiset.begin()); VectIt it(values.begin()), itend(values.end()); //Test the objects inserted in the base hook treap_set for(; it != itend; ++it, ++rbit) if(&*rbit != &*it) return 1; //Test the objects inserted in the member hook treap_set for(it = values.begin(); it != itend; ++it, ++mit) if(&*mit != &*it) return 1; //Test priority order for(int i = 0; i < 100; ++i){ if(baseset.top()->get_priority() != static_cast<unsigned int>(i/10)) return 1; if(membermultiset.top()->get_priority() != 9u - static_cast<unsigned int>(i/10)) return 1; baseset.erase(baseset.top()); membermultiset.erase(membermultiset.top()); } } return 0; }