Intrusive treap based associative containers: treap_set, treap_multiset and treap

| Home | Libraries | People | FAQ | More

## Intrusive treap based associative containers: treap_set, treap_multiset and treap

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 v is a left descendant of u, then key[v] < key[u];
- If v is a right descendant of u, then key[v] > key[u];
- If v is a child of u, then priority[v] <= priority[u];

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).

Boost.Intrusive treap associative containers don't use their own hook types but plain Binary search tree hooks. See Binary search tree hooks: bs_set_base_hook and bs_set_member_hook section for more information about these hooks.

### treap_set, treap_multiset and treap containers

    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<key_type> >`
- `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<key_type> >`
- `key_of_value<class KeyOfValueFunctionObject>`: A function object that will define the `key_type` of the value type to be stored. This type will allow a map-like interface. See Map and multimap-like interface with set and multiset for details. Default: `key_type` is equal to `value_type` (set-like interface).

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 the 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 {

### Exception safety of treap-based intrusive containers

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:

- If the priority order functor does not throw, treap-based containers, offer exactly the same guarantees as other tree-based containers.
- If the priority order functor throws, treap-based containers offer strong guarantee.

### Example

Now let's see a small example using binary search tree hooks and `treap_set`/ `treap_multiset` containers:

    #include <boost/intrusive/treap_set.hpp>
    #include <vector>
    #include <functional>
    #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;
    }