C++ sorted vector: Difference between revisions
(Created page with "The following class extends std::vector, adding sorting support. Most importantly to me, it can do an actual search in a logical manner, unlike the out-of-the-box functionality ...") |
No edit summary |
||
Line 1: | Line 1: | ||
The following class extends std::vector, adding sorting support. Most importantly to me, it can do an actual search in a logical manner, unlike the out-of-the-box functionality you get from the Standard Template Library. Check the code comments for more. | The following class extends std::vector, adding sorting support. Most importantly to me, it can do an actual search in a logical manner, unlike the out-of-the-box functionality you get from the Standard Template Library. Check the code comments for more. | ||
<pre>< | <pre> | ||
//-------------------------------------------------------------------// | |||
// Copyright © 2001-2011 A better Software. | |||
// | |||
// Use at your own risk. :> | |||
//-------------------------------------------------------------------// | |||
#if !defined(__SORTED_VECTOR_H) | |||
#define __SORTED_VECTOR_H | |||
#include <vector> | |||
#include <algorithm> // For lower_bound | |||
Line 18: | Line 29: | ||
// | // | ||
// METHOD 2 | // METHOD 2 | ||
// Allow batch insertion without sorting with push_unsorted(); then | // Allow batch insertion without sorting with push_unsorted(); then | ||
// provide an additional call to sort the vector; before | // provide an additional call to sort the vector; before | ||
// searching for an item, the vector is always sorted if needed; | // searching for an item, the vector is always sorted if needed; | ||
Line 79: | Line 90: | ||
class sorted_vector : public std::vector<T, _A> | class sorted_vector : public std::vector<T, _A> | ||
{ | { | ||
typedef std::vector<T> inherited; | |||
public: | public: | ||
//-------------------------------------------------------------------// | |||
// SetSorted() // | |||
//-------------------------------------------------------------------// | |||
// I didn't want to override every constructor to set this | |||
// member variable, so this function is publicly accessible. | |||
// You should call SetSorted( true ) right after construction. | |||
// TO DO: if you feel like it... derive all constructors to avoid | |||
// the need for this. There are 4 last time I checked. | |||
//-------------------------------------------------------------------// | |||
void SetSorted( bool bSorted = true ) { m_bSorted = bSorted; } | |||
//-------------------------------------------------------------------// | |||
// sort() // | // sort() // | ||
//-------------------------------------------------------------------// | |||
// This function sorts the data as needed. Call it after repeated calls to | // This function sorts the data as needed. Call it after repeated calls to | ||
// push_unsorted(), or just let other members call it for you on next access. | // push_unsorted(), or just let other members call it for you on next access. | ||
// It calls std::sort(), which defaults to using operator<() for | // It calls std::sort(), which defaults to using operator<() for | ||
// comparisons. | // comparisons. | ||
//-------------------------------------------------------------------// | |||
void sort() | |||
{ | |||
if ( !m_bSorted ) | |||
{ | |||
std::sort( inherited::begin(), inherited::end() ); | std::sort( inherited::begin(), inherited::end() ); | ||
SetSorted(); | |||
} | |||
} | |||
// This function is stupid. binary_search() was a dumb design, STL peeps. | // This function is stupid. binary_search() was a dumb design, STL peeps. | ||
bool bContains( const T& t ) | |||
{ | |||
if ( !m_bSorted ) | |||
{ | |||
sort(); | |||
} | |||
return std::binary_search( inherited::begin(), inherited::end(), t ); | |||
} | |||
/*const*/ T* lower_bound( const T& key ) | /*const*/ T* lower_bound( const T& key ) | ||
Line 154: | Line 165: | ||
// This is the function you want to use most of the time | // This is the function you want to use most of the time | ||
// (or the predicate version if you are using object pointers). | // (or the predicate version if you are using object pointers). | ||
// | // | ||
// USAGE: it makes most sense to use this function if you have | // USAGE: it makes most sense to use this function if you have | ||
Line 190: | Line 197: | ||
// using push_unsorted() for each, then calling sort(). | // using push_unsorted() for each, then calling sort(). | ||
//-------------------------------------------------------------------// | //-------------------------------------------------------------------// | ||
void push_sorted( const T& t ) | |||
{ | |||
if ( !m_bSorted ) | |||
{ | |||
sort(); | |||
} | |||
// Insert at "lower_bound" (the proper sorted location). | |||
insert( std::lower_bound( inherited::begin(), inherited::end(), t ), t ); | |||
} | |||
//-------------------------------------------------------------------// | |||
// push_unsorted() // | |||
//-------------------------------------------------------------------// | |||
// This is similar to push_back(), but in addition, it sets the | |||
// unsorted flag. | |||
//-------------------------------------------------------------------// | |||
void push_unsorted( const T& t ) | |||
{ | |||
SetSorted( false ); | |||
push_back(t); | push_back(t); | ||
} | |||
//-------------------------------------------------------------------// | //-------------------------------------------------------------------// | ||
Line 290: | Line 297: | ||
protected: | protected: | ||
bool m_bSorted; | |||
}; | }; | ||
#endif // __SORTED_VECTOR_H | |||
</pre> |
Revision as of 20:38, 21 January 2011
The following class extends std::vector, adding sorting support. Most importantly to me, it can do an actual search in a logical manner, unlike the out-of-the-box functionality you get from the Standard Template Library. Check the code comments for more.
//-------------------------------------------------------------------// // Copyright © 2001-2011 A better Software. // // Use at your own risk. :> //-------------------------------------------------------------------// #if !defined(__SORTED_VECTOR_H) #define __SORTED_VECTOR_H #include <vector> #include <algorithm> // For lower_bound //-------------------------------------------------------------------// // sorted_vector // //-------------------------------------------------------------------// // This class is derived from std::vector, and adds sort tracking. // There are two basic ways to use a sorted vector: // // METHOD 1 // Always maintain sort order by inserting with push_sorted() - // the location of new items is determined before inserting; // since the vector remains sorted, this doesn't take too long // (although for large batch insertions METHOD 2 is definitely // faster); // // METHOD 2 // Allow batch insertion without sorting with push_unsorted(); then // provide an additional call to sort the vector; before // searching for an item, the vector is always sorted if needed; // // Of course you need to provide an operator()< for the type of object // you're sorting, if it doesn't have one. Example: /* class MyClass { public: bool operator< (const MyClass left) const { if ( left.m_nMostImportant == m_nMostImportant ) return left.m_nLeastImportant < m_nLeastImportant; return left.m_nMostImportant < m_nMostImportant; } int m_nMostImportant; int m_nLeastImportant; } */ // // NOTE: C++ doesn't let you use an operator()< for POINTERS. This // breaks down when creating the template code, as you end up with // a ref to a ref which is not allowed (or something :P). // So if you have a vector of pointers, here's what you have to do: // // FOR NOW, with straight C++, create a less-than functor, then // pass that in to the functor versions of the class methods below. // Create a functor, aka function object, as follows: // // struct my_class_lessthan // { // bool operator()(const MyClass* left, const MyClass* right) // { // return left->get_timestamp() < right->get_timestamp(); // } // }; // // Usage example: // // sorted_vector<MyClass*> svpMC; // svpMC.push_unsorted(new MyClass(blah, blah); // svpMC.push_unsorted(new MyClass(blah, blah); // vpMC.sort( my_class_lessthan() ); // // Once C++0x is available, I need to update this class to use // a function object wrapper, and allow the user to set it // in the constructor, then always use it automatically. // http://en.wikipedia.org/wiki/C%2B%2B0x#Polymorphic_wrappers_for_function_objects // // FUN NOTE: you can cause an internal compiler error if you leave off // the part bracketed by #... // // class sorted_vector : public std::vector#<T># // //-------------------------------------------------------------------// template<class T, class _A = std::allocator<T> > class sorted_vector : public std::vector<T, _A> { typedef std::vector<T> inherited; public: //-------------------------------------------------------------------// // SetSorted() // //-------------------------------------------------------------------// // I didn't want to override every constructor to set this // member variable, so this function is publicly accessible. // You should call SetSorted( true ) right after construction. // TO DO: if you feel like it... derive all constructors to avoid // the need for this. There are 4 last time I checked. //-------------------------------------------------------------------// void SetSorted( bool bSorted = true ) { m_bSorted = bSorted; } //-------------------------------------------------------------------// // sort() // //-------------------------------------------------------------------// // This function sorts the data as needed. Call it after repeated calls to // push_unsorted(), or just let other members call it for you on next access. // It calls std::sort(), which defaults to using operator<() for // comparisons. //-------------------------------------------------------------------// void sort() { if ( !m_bSorted ) { std::sort( inherited::begin(), inherited::end() ); SetSorted(); } } // This function is stupid. binary_search() was a dumb design, STL peeps. bool bContains( const T& t ) { if ( !m_bSorted ) { sort(); } return std::binary_search( inherited::begin(), inherited::end(), t ); } /*const*/ T* lower_bound( const T& key ) { if ( !m_bSorted ) sort(); typename std::vector<T>::iterator it = std::lower_bound( inherited::begin(), inherited::end(), key ); if (it==inherited::end()) return 0; /*const*/ T* t = &(*it); return t; } typename std::vector<T>::iterator lower_bound_it( const T& key ) { if ( !m_bSorted ) sort(); typename std::vector<T>::iterator it = std::lower_bound( inherited::begin(), inherited::end(), key ); return it; } //-------------------------------------------------------------------// // find_it_or_fail() // //-------------------------------------------------------------------// // This function takes the given object and determines if there is // a match in the vector. It returns an iterator to the actual // object in the vector, if found. Otherwise returns std::vector::end(). // // This is the function you want to use most of the time // (or the predicate version if you are using object pointers). // // USAGE: it makes most sense to use this function if you have // an object with a key, other member variables, and operator<() // that uses the key to test for equality. You then set up a dummy // "search" object with the key set to the search value, call the // function, and use the result to extract the additional information // from the object. // // WARNING: if you change the key value of any object in the vector, // you have unsorted the array without marking it as such. Make sure // you call SetSorted(false) where appropriate. // //-------------------------------------------------------------------// typename std::vector<T>::iterator find_it_or_fail( const T& key ) { typename std::vector<T>::iterator it = lower_bound_it( key ); if ((*it)==key) return it; else return inherited::end(); } //-------------------------------------------------------------------// // push_sorted() // //-------------------------------------------------------------------// // This is used to insert into a vector that always remains sorted. // Because we have a sorted vector, finding the insertion location // with std::lower_bound() is relatively cheap. // // If you have multiple insertions, consider // using push_unsorted() for each, then calling sort(). //-------------------------------------------------------------------// void push_sorted( const T& t ) { if ( !m_bSorted ) { sort(); } // Insert at "lower_bound" (the proper sorted location). insert( std::lower_bound( inherited::begin(), inherited::end(), t ), t ); } //-------------------------------------------------------------------// // push_unsorted() // //-------------------------------------------------------------------// // This is similar to push_back(), but in addition, it sets the // unsorted flag. //-------------------------------------------------------------------// void push_unsorted( const T& t ) { SetSorted( false ); push_back(t); } //-------------------------------------------------------------------// // operator=() // //-------------------------------------------------------------------// // This allows us to set the sorted_vector from a std::vector. //-------------------------------------------------------------------// sorted_vector<T>& operator=(std::vector<T>& v) { typename std::vector<T>::iterator it; for (it= v.begin(); it != v.end(); ++it) push_unsorted((*it)); return this; } // CALLS WHERE YOU PROVIDE THE FUNCTOR OR FUNCTION POINTER // If you need to use a predicate sort function, ALWAYS use these methods // instead of the non-functor versions. // NOTE: UPDATE THIS SHITTINESS when C++0x polymorphic function wrappers are available. template<class _Pr> inline void sort( _Pr pr ) { if ( !m_bSorted ) { std::sort( inherited::begin(), inherited::end(), pr ); SetSorted(); } } template<class _Pr> inline /*const*/ T* lower_bound( const T& key, _Pr pr ) { if ( !m_bSorted ) { std::sort( inherited::begin(), inherited::end(), pr ); SetSorted(); } typename std::vector<T>::iterator it = std::lower_bound( inherited::begin(), inherited::end(), key, pr ); if (it==inherited::end()) return 0; /*const*/ T* t = &(*it); return t; } template<class _Pr> inline typename std::vector<T>::iterator lower_bound_it( const T& key, _Pr pr ) { if ( !m_bSorted ) { std::sort( inherited::begin(), inherited::end(), pr ); SetSorted(); } typename std::vector<T>::iterator it = std::lower_bound( inherited::begin(), inherited::end(), key, pr ); return it; } template<class _Pr> inline void push_sorted( const T& t, _Pr pr ) { if ( !m_bSorted ) { std::sort( inherited::begin(), inherited::end(), pr ); SetSorted(); } // Insert at "lower_bound" (the proper sorted location). insert( std::lower_bound( inherited::begin(), inherited::end(), t, pr ), t ); } template<class _Pr> inline typename std::vector<T>::iterator find_it_or_fail( const T& key, _Pr pr ) { typename std::vector<T>::iterator it = lower_bound_it( key, pr ); if ((*it)==key) return it; else return inherited::end(); } protected: bool m_bSorted; }; #endif // __SORTED_VECTOR_H