Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Intrusive treap based associative containers: treap_set, treap_multiset and treap

Using binary search tree hooks: bs_set_base_hook and bs_set_member_hook
treap_set, treap_multiset and treap containers
Exception safety of treap-based intrusive containers
Example

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:

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

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()), rbitend(baseset.rend());
      MemberMultiset::iterator mit(membermultiset.begin()), mitend(membermultiset.end());
      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;
}


PrevUpHomeNext