2022-08-12 15:53:58 +02:00
|
|
|
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
|
|
//
|
|
|
|
// Purpose:
|
|
|
|
//
|
|
|
|
// $Header: $
|
|
|
|
// $NoKeywords: $
|
|
|
|
//=============================================================================//
|
|
|
|
|
|
|
|
#ifndef UTLRBTREE_H
|
|
|
|
#define UTLRBTREE_H
|
|
|
|
|
|
|
|
#include "tier1/utlmemory.h"
|
|
|
|
#include "tier1/utlfixedmemory.h"
|
|
|
|
#include "tier1/utlblockmemory.h"
|
|
|
|
|
|
|
|
|
|
|
|
// This is a useful macro to iterate from start to end in order in a map
|
|
|
|
#define FOR_EACH_UTLRBTREE( treeName, iteratorName ) \
|
|
|
|
for ( int iteratorName = treeName.FirstInorder(); iteratorName != treeName.InvalidIndex(); iteratorName = treeName.NextInorder( iteratorName ) )
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Tool to generate a default compare function for any type that implements
|
|
|
|
// operator<, including all simple types
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template <typename T >
|
|
|
|
class CDefOps
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static bool LessFunc(const T& lhs, const T& rhs) { return (lhs < rhs); }
|
|
|
|
};
|
|
|
|
|
|
|
|
#define DefLessFunc( type ) CDefOps< type >::LessFunc
|
|
|
|
|
|
|
|
//-------------------------------------
|
|
|
|
|
|
|
|
inline bool StringLessThan(const char* const& lhs, const char* const& rhs) {
|
|
|
|
if (!lhs) return false;
|
|
|
|
if (!rhs) return true;
|
|
|
|
return (strcmp(lhs, rhs) < 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool CaselessStringLessThan(const char* const& lhs, const char* const& rhs) {
|
|
|
|
if (!lhs) return false;
|
|
|
|
if (!rhs) return true;
|
|
|
|
return (_stricmp(lhs, rhs) < 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Same as CaselessStringLessThan, but it ignores differences in / and \.
|
|
|
|
inline bool CaselessStringLessThanIgnoreSlashes(const char* const& lhs, const char* const& rhs)
|
|
|
|
{
|
|
|
|
const char* pa = lhs;
|
|
|
|
const char* pb = rhs;
|
|
|
|
while (*pa && *pb)
|
|
|
|
{
|
|
|
|
char a = *pa;
|
|
|
|
char b = *pb;
|
|
|
|
|
|
|
|
// Check for dir slashes.
|
|
|
|
if (a == '/' || a == '\\')
|
|
|
|
{
|
|
|
|
if (b != '/' && b != '\\')
|
|
|
|
return ('/' < b);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (a >= 'a' && a <= 'z')
|
|
|
|
a = 'A' + (a - 'a');
|
|
|
|
|
|
|
|
if (b >= 'a' && b <= 'z')
|
|
|
|
b = 'A' + (b - 'a');
|
|
|
|
|
|
|
|
if (a > b)
|
|
|
|
return false;
|
|
|
|
else if (a < b)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
++pa;
|
|
|
|
++pb;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Filenames also must be the same length.
|
|
|
|
if (*pa != *pb)
|
|
|
|
{
|
|
|
|
// If pa shorter than pb then it's "less"
|
|
|
|
return (!*pa);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------
|
|
|
|
// inline these two templates to stop multiple definitions of the same code
|
|
|
|
template <> inline bool CDefOps<const char*>::LessFunc(const char* const& lhs, const char* const& rhs) { return StringLessThan(lhs, rhs); }
|
|
|
|
template <> inline bool CDefOps<char*>::LessFunc(char* const& lhs, char* const& rhs) { return StringLessThan(lhs, rhs); }
|
|
|
|
|
|
|
|
//-------------------------------------
|
|
|
|
|
|
|
|
template <typename RBTREE_T>
|
|
|
|
void SetDefLessFunc(RBTREE_T& RBTree)
|
|
|
|
{
|
|
|
|
RBTree.SetLessFunc(DefLessFunc(typename RBTREE_T::KeyType_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// A red-black binary search tree
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class I >
|
|
|
|
struct UtlRBTreeLinks_t
|
|
|
|
{
|
|
|
|
I m_Left;
|
|
|
|
I m_Right;
|
|
|
|
I m_Parent;
|
|
|
|
I m_Tag;
|
|
|
|
};
|
|
|
|
|
|
|
|
template < class T, class I >
|
|
|
|
struct UtlRBTreeNode_t : public UtlRBTreeLinks_t< I >
|
|
|
|
{
|
|
|
|
T m_Data;
|
|
|
|
};
|
|
|
|
|
|
|
|
template < class T, class I = unsigned short, typename L = bool (*)(const T&, const T&), class M = CUtlMemory< UtlRBTreeNode_t< T, I >, I > >
|
|
|
|
class CUtlRBTree
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
typedef T KeyType_t;
|
|
|
|
typedef T ElemType_t;
|
|
|
|
typedef I IndexType_t;
|
|
|
|
|
|
|
|
// Less func typedef
|
|
|
|
// Returns true if the first parameter is "less" than the second
|
|
|
|
typedef L LessFunc_t;
|
|
|
|
|
|
|
|
// constructor, destructor
|
|
|
|
// Left at growSize = 0, the memory will first allocate 1 element and double in size
|
|
|
|
// at each increment.
|
|
|
|
// LessFunc_t is required, but may be set after the constructor using SetLessFunc() below
|
|
|
|
CUtlRBTree(int growSize = 0, int initSize = 0, const LessFunc_t& lessfunc = 0);
|
|
|
|
CUtlRBTree(const LessFunc_t& lessfunc);
|
|
|
|
~CUtlRBTree();
|
|
|
|
|
|
|
|
void EnsureCapacity(int num);
|
|
|
|
|
|
|
|
// NOTE: CopyFrom is fast but dangerous! It just memcpy's all nodes - it does NOT run copy constructors, so
|
|
|
|
// it is not a true deep copy (i.e 'T' must be POD for this to work - e.g CUtlString will not work).
|
|
|
|
void CopyFrom(const CUtlRBTree<T, I, L, M>& other);
|
|
|
|
|
|
|
|
// gets particular elements
|
|
|
|
T& Element(I i);
|
|
|
|
T const& Element(I i) const;
|
|
|
|
T& operator[](I i);
|
|
|
|
T const& operator[](I i) const;
|
|
|
|
|
|
|
|
// Gets the root
|
|
|
|
I Root() const;
|
|
|
|
|
|
|
|
// Num elements
|
|
|
|
unsigned int Count() const;
|
|
|
|
|
|
|
|
// Max "size" of the vector
|
|
|
|
// it's not generally safe to iterate from index 0 to MaxElement()-1 (you could do this as a potential
|
|
|
|
// iteration optimization, IF CUtlMemory is the allocator, and IF IsValidIndex() is tested for each element...
|
|
|
|
// but this should be implemented inside the CUtlRBTree iteration API, if anywhere)
|
|
|
|
I MaxElement() const;
|
|
|
|
|
|
|
|
// Gets the children
|
|
|
|
I Parent(I i) const;
|
|
|
|
I LeftChild(I i) const;
|
|
|
|
I RightChild(I i) const;
|
|
|
|
|
|
|
|
// Tests if a node is a left or right child
|
|
|
|
bool IsLeftChild(I i) const;
|
|
|
|
bool IsRightChild(I i) const;
|
|
|
|
|
|
|
|
// Tests if root or leaf
|
|
|
|
bool IsRoot(I i) const;
|
|
|
|
bool IsLeaf(I i) const;
|
|
|
|
|
|
|
|
// Checks if a node is valid and in the tree
|
|
|
|
bool IsValidIndex(I i) const;
|
|
|
|
|
|
|
|
// Checks if the tree as a whole is valid
|
|
|
|
bool IsValid() const;
|
|
|
|
|
|
|
|
// Invalid index
|
|
|
|
static I InvalidIndex();
|
|
|
|
|
|
|
|
// returns the tree depth (not a very fast operation)
|
|
|
|
int Depth(I node) const;
|
|
|
|
int Depth() const;
|
|
|
|
|
|
|
|
// Sets the less func
|
|
|
|
void SetLessFunc(const LessFunc_t& func);
|
|
|
|
|
|
|
|
// Allocation method
|
|
|
|
I NewNode();
|
|
|
|
|
|
|
|
// Insert method (inserts in order)
|
|
|
|
// NOTE: the returned 'index' will be valid as long as the element remains in the tree
|
|
|
|
// (other elements being added/removed will not affect it)
|
|
|
|
I Insert(T const& insert);
|
|
|
|
void Insert(const T* pArray, int nItems);
|
|
|
|
I InsertIfNotFound(T const& insert);
|
|
|
|
|
|
|
|
// Find method
|
|
|
|
I Find(T const& search) const;
|
|
|
|
|
|
|
|
// Remove methods
|
|
|
|
void RemoveAt(I i);
|
|
|
|
bool Remove(T const& remove);
|
|
|
|
void RemoveAll();
|
|
|
|
void Purge();
|
|
|
|
|
|
|
|
// Allocation, deletion
|
|
|
|
void FreeNode(I i);
|
|
|
|
|
|
|
|
// Iteration
|
|
|
|
I FirstInorder() const;
|
|
|
|
I NextInorder(I i) const;
|
|
|
|
I PrevInorder(I i) const;
|
|
|
|
I LastInorder() const;
|
|
|
|
|
|
|
|
I FirstPreorder() const;
|
|
|
|
I NextPreorder(I i) const;
|
|
|
|
I PrevPreorder(I i) const;
|
|
|
|
I LastPreorder() const;
|
|
|
|
|
|
|
|
I FirstPostorder() const;
|
|
|
|
I NextPostorder(I i) const;
|
|
|
|
|
|
|
|
// If you change the search key, this can be used to reinsert the
|
|
|
|
// element into the tree.
|
|
|
|
void Reinsert(I elem);
|
|
|
|
|
|
|
|
// swap in place
|
|
|
|
void Swap(CUtlRBTree< T, I, L >& that);
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Can't copy the tree this way!
|
|
|
|
CUtlRBTree<T, I, L, M>& operator=(const CUtlRBTree<T, I, L, M>& other);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
enum NodeColor_t
|
|
|
|
{
|
|
|
|
RED = 0,
|
|
|
|
BLACK
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef UtlRBTreeNode_t< T, I > Node_t;
|
|
|
|
typedef UtlRBTreeLinks_t< I > Links_t;
|
|
|
|
|
|
|
|
// Sets the children
|
|
|
|
void SetParent(I i, I parent);
|
|
|
|
void SetLeftChild(I i, I child);
|
|
|
|
void SetRightChild(I i, I child);
|
|
|
|
void LinkToParent(I i, I parent, bool isLeft);
|
|
|
|
|
|
|
|
// Gets at the links
|
|
|
|
Links_t const& Links(I i) const;
|
|
|
|
Links_t& Links(I i);
|
|
|
|
|
|
|
|
// Checks if a link is red or black
|
|
|
|
bool IsRed(I i) const;
|
|
|
|
bool IsBlack(I i) const;
|
|
|
|
|
|
|
|
// Sets/gets node color
|
|
|
|
NodeColor_t Color(I i) const;
|
|
|
|
void SetColor(I i, NodeColor_t c);
|
|
|
|
|
|
|
|
// operations required to preserve tree balance
|
|
|
|
void RotateLeft(I i);
|
|
|
|
void RotateRight(I i);
|
|
|
|
void InsertRebalance(I i);
|
|
|
|
void RemoveRebalance(I i);
|
|
|
|
|
|
|
|
// Insertion, removal
|
|
|
|
I InsertAt(I parent, bool leftchild);
|
|
|
|
|
|
|
|
// copy constructors not allowed
|
|
|
|
CUtlRBTree(CUtlRBTree<T, I, L, M> const& tree);
|
|
|
|
|
|
|
|
// Inserts a node into the tree, doesn't copy the data in.
|
|
|
|
void FindInsertionPosition(T const& insert, I& parent, bool& leftchild);
|
|
|
|
|
|
|
|
// Remove and add back an element in the tree.
|
|
|
|
void Unlink(I elem);
|
|
|
|
void Link(I elem);
|
|
|
|
|
|
|
|
// Used for sorting.
|
|
|
|
LessFunc_t m_LessFunc;
|
|
|
|
|
|
|
|
M m_Elements;
|
|
|
|
I m_Root;
|
|
|
|
I m_NumElements;
|
|
|
|
I m_FirstFree;
|
|
|
|
typename M::Iterator_t m_LastAlloc; // the last index allocated
|
|
|
|
|
|
|
|
Node_t* m_pElements;
|
|
|
|
|
|
|
|
FORCEINLINE M const& Elements(void) const
|
|
|
|
{
|
|
|
|
return m_Elements;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ResetDbgInfo()
|
|
|
|
{
|
|
|
|
m_pElements = (Node_t*)m_Elements.Base();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice
|
|
|
|
template < class T, class I = int, typename L = bool (*)(const T&, const T&) >
|
|
|
|
class CUtlFixedRBTree : public CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > >
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
typedef L LessFunc_t;
|
|
|
|
|
|
|
|
CUtlFixedRBTree(int growSize = 0, int initSize = 0, const LessFunc_t& lessfunc = 0)
|
|
|
|
: CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > >(growSize, initSize, lessfunc) {}
|
|
|
|
CUtlFixedRBTree(const LessFunc_t& lessfunc)
|
|
|
|
: CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > >(lessfunc) {}
|
|
|
|
|
|
|
|
typedef CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > > BaseClass;
|
|
|
|
bool IsValidIndex(I i) const
|
|
|
|
{
|
|
|
|
if (!BaseClass::Elements().IsIdxValid(i))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
#ifdef _DEBUG // it's safe to skip this here, since the only way to get indices after m_LastAlloc is to use MaxElement()
|
|
|
|
if (BaseClass::Elements().IsIdxAfter(i, this->m_LastAlloc))
|
|
|
|
{
|
|
|
|
Assert(0);
|
|
|
|
return false; // don't read values that have been allocated, but not constructed
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return LeftChild(i) != i;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void ResetDbgInfo() {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// this doesn't make sense for fixed rbtrees, since there's no useful max pointer, and the index space isn't contiguous anyways
|
|
|
|
I MaxElement() const;
|
|
|
|
};
|
|
|
|
|
|
|
|
// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice
|
|
|
|
template < class T, class I = unsigned short, typename L = bool (*)(const T&, const T&) >
|
|
|
|
class CUtlBlockRBTree : public CUtlRBTree< T, I, L, CUtlBlockMemory< UtlRBTreeNode_t< T, I >, I > >
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
typedef L LessFunc_t;
|
|
|
|
CUtlBlockRBTree(int growSize = 0, int initSize = 0, const LessFunc_t& lessfunc = 0)
|
|
|
|
: CUtlRBTree< T, I, L, CUtlBlockMemory< UtlRBTreeNode_t< T, I >, I > >(growSize, initSize, lessfunc) {}
|
|
|
|
CUtlBlockRBTree(const LessFunc_t& lessfunc)
|
|
|
|
: CUtlRBTree< T, I, L, CUtlBlockMemory< UtlRBTreeNode_t< T, I >, I > >(lessfunc) {}
|
|
|
|
protected:
|
|
|
|
void ResetDbgInfo() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// constructor, destructor
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline CUtlRBTree<T, I, L, M>::CUtlRBTree(int growSize, int initSize, const LessFunc_t& lessfunc) :
|
|
|
|
m_LessFunc(lessfunc),
|
|
|
|
m_Elements(growSize, initSize),
|
|
|
|
m_Root(InvalidIndex()),
|
|
|
|
m_NumElements(0),
|
|
|
|
m_FirstFree(InvalidIndex()),
|
|
|
|
m_LastAlloc(m_Elements.InvalidIterator())
|
|
|
|
{
|
|
|
|
ResetDbgInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline CUtlRBTree<T, I, L, M>::CUtlRBTree(const LessFunc_t& lessfunc) :
|
|
|
|
m_Elements(0, 0),
|
|
|
|
m_LessFunc(lessfunc),
|
|
|
|
m_Root(InvalidIndex()),
|
|
|
|
m_NumElements(0),
|
|
|
|
m_FirstFree(InvalidIndex()),
|
|
|
|
m_LastAlloc(m_Elements.InvalidIterator())
|
|
|
|
{
|
|
|
|
ResetDbgInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline CUtlRBTree<T, I, L, M>::~CUtlRBTree()
|
|
|
|
{
|
|
|
|
Purge();
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline void CUtlRBTree<T, I, L, M>::EnsureCapacity(int num)
|
|
|
|
{
|
|
|
|
m_Elements.EnsureCapacity(num);
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline void CUtlRBTree<T, I, L, M>::CopyFrom(const CUtlRBTree<T, I, L, M>& other)
|
|
|
|
{
|
|
|
|
Purge();
|
|
|
|
m_Elements.EnsureCapacity(other.m_Elements.Count());
|
|
|
|
memcpy(m_Elements.Base(), other.m_Elements.Base(), other.m_Elements.Count() * sizeof(UtlRBTreeNode_t< T, I >));
|
|
|
|
m_LessFunc = other.m_LessFunc;
|
|
|
|
m_Root = other.m_Root;
|
|
|
|
m_NumElements = other.m_NumElements;
|
|
|
|
m_FirstFree = other.m_FirstFree;
|
|
|
|
m_LastAlloc = other.m_LastAlloc;
|
|
|
|
ResetDbgInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// gets particular elements
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline T& CUtlRBTree<T, I, L, M>::Element(I i)
|
|
|
|
{
|
|
|
|
return m_Elements[i].m_Data;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline T const& CUtlRBTree<T, I, L, M>::Element(I i) const
|
|
|
|
{
|
|
|
|
return m_Elements[i].m_Data;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline T& CUtlRBTree<T, I, L, M>::operator[](I i)
|
|
|
|
{
|
|
|
|
return Element(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline T const& CUtlRBTree<T, I, L, M>::operator[](I i) const
|
|
|
|
{
|
|
|
|
return Element(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// various accessors
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Gets the root
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline I CUtlRBTree<T, I, L, M>::Root() const
|
|
|
|
{
|
|
|
|
return m_Root;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Num elements
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline unsigned int CUtlRBTree<T, I, L, M>::Count() const
|
|
|
|
{
|
|
|
|
return (unsigned int)m_NumElements;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Max "size" of the vector
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline I CUtlRBTree<T, I, L, M>::MaxElement() const
|
|
|
|
{
|
|
|
|
return (I)m_Elements.NumAllocated();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Gets the children
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline I CUtlRBTree<T, I, L, M>::Parent(I i) const
|
|
|
|
{
|
|
|
|
return Links(i).m_Parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline I CUtlRBTree<T, I, L, M>::LeftChild(I i) const
|
|
|
|
{
|
|
|
|
return Links(i).m_Left;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline I CUtlRBTree<T, I, L, M>::RightChild(I i) const
|
|
|
|
{
|
|
|
|
return Links(i).m_Right;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Tests if a node is a left or right child
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline bool CUtlRBTree<T, I, L, M>::IsLeftChild(I i) const
|
|
|
|
{
|
|
|
|
return LeftChild(Parent(i)) == i;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline bool CUtlRBTree<T, I, L, M>::IsRightChild(I i) const
|
|
|
|
{
|
|
|
|
return RightChild(Parent(i)) == i;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Tests if root or leaf
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline bool CUtlRBTree<T, I, L, M>::IsRoot(I i) const
|
|
|
|
{
|
|
|
|
return i == m_Root;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline bool CUtlRBTree<T, I, L, M>::IsLeaf(I i) const
|
|
|
|
{
|
|
|
|
return (LeftChild(i) == InvalidIndex()) && (RightChild(i) == InvalidIndex());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Checks if a node is valid and in the tree
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline bool CUtlRBTree<T, I, L, M>::IsValidIndex(I i) const
|
|
|
|
{
|
|
|
|
if (!m_Elements.IsIdxValid(i))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (m_Elements.IsIdxAfter(i, m_LastAlloc))
|
|
|
|
return false; // don't read values that have been allocated, but not constructed
|
|
|
|
|
|
|
|
return LeftChild(i) != i;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Invalid index
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline I CUtlRBTree<T, I, L, M>::InvalidIndex()
|
|
|
|
{
|
|
|
|
return (I)M::InvalidIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// returns the tree depth (not a very fast operation)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline int CUtlRBTree<T, I, L, M>::Depth() const
|
|
|
|
{
|
|
|
|
return Depth(Root());
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Sets the children
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline void CUtlRBTree<T, I, L, M>::SetParent(I i, I parent)
|
|
|
|
{
|
|
|
|
Links(i).m_Parent = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline void CUtlRBTree<T, I, L, M>::SetLeftChild(I i, I child)
|
|
|
|
{
|
|
|
|
Links(i).m_Left = child;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline void CUtlRBTree<T, I, L, M>::SetRightChild(I i, I child)
|
|
|
|
{
|
|
|
|
Links(i).m_Right = child;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Gets at the links
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline typename CUtlRBTree<T, I, L, M>::Links_t const& CUtlRBTree<T, I, L, M>::Links(I i) const
|
|
|
|
{
|
|
|
|
// Sentinel node, makes life easier
|
|
|
|
static Links_t s_Sentinel =
|
|
|
|
{
|
|
|
|
InvalidIndex(), InvalidIndex(), InvalidIndex(), CUtlRBTree<T, I, L, M>::BLACK
|
|
|
|
};
|
|
|
|
|
|
|
|
return (i != InvalidIndex()) ? *(Links_t*)&m_Elements[i] : *(Links_t*)&s_Sentinel;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline typename CUtlRBTree<T, I, L, M>::Links_t& CUtlRBTree<T, I, L, M>::Links(I i)
|
|
|
|
{
|
|
|
|
Assert(i != InvalidIndex());
|
|
|
|
return *(Links_t*)&m_Elements[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Checks if a link is red or black
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline bool CUtlRBTree<T, I, L, M>::IsRed(I i) const
|
|
|
|
{
|
|
|
|
return (Links(i).m_Tag == RED);
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline bool CUtlRBTree<T, I, L, M>::IsBlack(I i) const
|
|
|
|
{
|
|
|
|
return (Links(i).m_Tag == BLACK);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Sets/gets node color
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline typename CUtlRBTree<T, I, L, M>::NodeColor_t CUtlRBTree<T, I, L, M>::Color(I i) const
|
|
|
|
{
|
|
|
|
return (NodeColor_t)Links(i).m_Tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
inline void CUtlRBTree<T, I, L, M>::SetColor(I i, typename CUtlRBTree<T, I, L, M>::NodeColor_t c)
|
|
|
|
{
|
|
|
|
Links(i).m_Tag = (I)c;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Allocates/ deallocates nodes
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#pragma warning(push)
|
|
|
|
#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::NewNode()
|
|
|
|
{
|
|
|
|
I elem;
|
|
|
|
|
|
|
|
// Nothing in the free list; add.
|
|
|
|
if (m_FirstFree == InvalidIndex())
|
|
|
|
{
|
|
|
|
Assert(m_Elements.IsValidIterator(m_LastAlloc) || m_NumElements == 0);
|
|
|
|
typename M::Iterator_t it = m_Elements.IsValidIterator(m_LastAlloc) ? m_Elements.Next(m_LastAlloc) : m_Elements.First();
|
|
|
|
if (!m_Elements.IsValidIterator(it))
|
|
|
|
{
|
|
|
|
MEM_ALLOC_CREDIT_CLASS();
|
|
|
|
m_Elements.Grow();
|
|
|
|
|
|
|
|
it = m_Elements.IsValidIterator(m_LastAlloc) ? m_Elements.Next(m_LastAlloc) : m_Elements.First();
|
|
|
|
|
|
|
|
Assert(m_Elements.IsValidIterator(it));
|
|
|
|
if (!m_Elements.IsValidIterator(it))
|
|
|
|
{
|
2022-08-22 12:42:41 +02:00
|
|
|
Error(eDLL_T::ENGINE, true, "CUtlRBTree overflow!\n");
|
2022-08-12 15:53:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
m_LastAlloc = it;
|
|
|
|
elem = m_Elements.GetIndex(m_LastAlloc);
|
|
|
|
Assert(m_Elements.IsValidIterator(m_LastAlloc));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
elem = m_FirstFree;
|
|
|
|
m_FirstFree = Links(m_FirstFree).m_Right;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
// reset links to invalid....
|
|
|
|
Links_t& node = Links(elem);
|
|
|
|
node.m_Left = node.m_Right = node.m_Parent = InvalidIndex();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Construct(&Element(elem));
|
|
|
|
ResetDbgInfo();
|
|
|
|
|
|
|
|
return elem;
|
|
|
|
}
|
|
|
|
#pragma warning(pop)
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::FreeNode(I i)
|
|
|
|
{
|
|
|
|
Assert(IsValidIndex(i) && (i != InvalidIndex()));
|
|
|
|
Destruct(&Element(i));
|
|
|
|
SetLeftChild(i, i); // indicates it's in not in the tree
|
|
|
|
SetRightChild(i, m_FirstFree);
|
|
|
|
m_FirstFree = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Rotates node i to the left
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::RotateLeft(I elem)
|
|
|
|
{
|
|
|
|
I rightchild = RightChild(elem);
|
|
|
|
SetRightChild(elem, LeftChild(rightchild));
|
|
|
|
if (LeftChild(rightchild) != InvalidIndex())
|
|
|
|
SetParent(LeftChild(rightchild), elem);
|
|
|
|
|
|
|
|
if (rightchild != InvalidIndex())
|
|
|
|
SetParent(rightchild, Parent(elem));
|
|
|
|
if (!IsRoot(elem))
|
|
|
|
{
|
|
|
|
if (IsLeftChild(elem))
|
|
|
|
SetLeftChild(Parent(elem), rightchild);
|
|
|
|
else
|
|
|
|
SetRightChild(Parent(elem), rightchild);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_Root = rightchild;
|
|
|
|
|
|
|
|
SetLeftChild(rightchild, elem);
|
|
|
|
if (elem != InvalidIndex())
|
|
|
|
SetParent(elem, rightchild);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Rotates node i to the right
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::RotateRight(I elem)
|
|
|
|
{
|
|
|
|
I leftchild = LeftChild(elem);
|
|
|
|
SetLeftChild(elem, RightChild(leftchild));
|
|
|
|
if (RightChild(leftchild) != InvalidIndex())
|
|
|
|
SetParent(RightChild(leftchild), elem);
|
|
|
|
|
|
|
|
if (leftchild != InvalidIndex())
|
|
|
|
SetParent(leftchild, Parent(elem));
|
|
|
|
if (!IsRoot(elem))
|
|
|
|
{
|
|
|
|
if (IsRightChild(elem))
|
|
|
|
SetRightChild(Parent(elem), leftchild);
|
|
|
|
else
|
|
|
|
SetLeftChild(Parent(elem), leftchild);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_Root = leftchild;
|
|
|
|
|
|
|
|
SetRightChild(leftchild, elem);
|
|
|
|
if (elem != InvalidIndex())
|
|
|
|
SetParent(elem, leftchild);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Rebalances the tree after an insertion
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::InsertRebalance(I elem)
|
|
|
|
{
|
|
|
|
while (!IsRoot(elem) && (Color(Parent(elem)) == RED))
|
|
|
|
{
|
|
|
|
I parent = Parent(elem);
|
|
|
|
I grandparent = Parent(parent);
|
|
|
|
|
|
|
|
/* we have a violation */
|
|
|
|
if (IsLeftChild(parent))
|
|
|
|
{
|
|
|
|
I uncle = RightChild(grandparent);
|
|
|
|
if (IsRed(uncle))
|
|
|
|
{
|
|
|
|
/* uncle is RED */
|
|
|
|
SetColor(parent, BLACK);
|
|
|
|
SetColor(uncle, BLACK);
|
|
|
|
SetColor(grandparent, RED);
|
|
|
|
elem = grandparent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* uncle is BLACK */
|
|
|
|
if (IsRightChild(elem))
|
|
|
|
{
|
|
|
|
/* make x a left child, will change parent and grandparent */
|
|
|
|
elem = parent;
|
|
|
|
RotateLeft(elem);
|
|
|
|
parent = Parent(elem);
|
|
|
|
grandparent = Parent(parent);
|
|
|
|
}
|
|
|
|
/* recolor and rotate */
|
|
|
|
SetColor(parent, BLACK);
|
|
|
|
SetColor(grandparent, RED);
|
|
|
|
RotateRight(grandparent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* mirror image of above code */
|
|
|
|
I uncle = LeftChild(grandparent);
|
|
|
|
if (IsRed(uncle))
|
|
|
|
{
|
|
|
|
/* uncle is RED */
|
|
|
|
SetColor(parent, BLACK);
|
|
|
|
SetColor(uncle, BLACK);
|
|
|
|
SetColor(grandparent, RED);
|
|
|
|
elem = grandparent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* uncle is BLACK */
|
|
|
|
if (IsLeftChild(elem))
|
|
|
|
{
|
|
|
|
/* make x a right child, will change parent and grandparent */
|
|
|
|
elem = parent;
|
|
|
|
RotateRight(parent);
|
|
|
|
parent = Parent(elem);
|
|
|
|
grandparent = Parent(parent);
|
|
|
|
}
|
|
|
|
/* recolor and rotate */
|
|
|
|
SetColor(parent, BLACK);
|
|
|
|
SetColor(grandparent, RED);
|
|
|
|
RotateLeft(grandparent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SetColor(m_Root, BLACK);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Insert a node into the tree
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::InsertAt(I parent, bool leftchild)
|
|
|
|
{
|
|
|
|
I i = NewNode();
|
|
|
|
LinkToParent(i, parent, leftchild);
|
|
|
|
++m_NumElements;
|
|
|
|
|
|
|
|
Assert(IsValid());
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::LinkToParent(I i, I parent, bool isLeft)
|
|
|
|
{
|
|
|
|
Links_t& elem = Links(i);
|
|
|
|
elem.m_Parent = parent;
|
|
|
|
elem.m_Left = elem.m_Right = InvalidIndex();
|
|
|
|
elem.m_Tag = RED;
|
|
|
|
|
|
|
|
/* insert node in tree */
|
|
|
|
if (parent != InvalidIndex())
|
|
|
|
{
|
|
|
|
if (isLeft)
|
|
|
|
Links(parent).m_Left = i;
|
|
|
|
else
|
|
|
|
Links(parent).m_Right = i;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_Root = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
InsertRebalance(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Rebalance the tree after a deletion
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::RemoveRebalance(I elem)
|
|
|
|
{
|
|
|
|
while (elem != m_Root && IsBlack(elem))
|
|
|
|
{
|
|
|
|
I parent = Parent(elem);
|
|
|
|
|
|
|
|
// If elem is the left child of the parent
|
|
|
|
if (elem == LeftChild(parent))
|
|
|
|
{
|
|
|
|
// Get our sibling
|
|
|
|
I sibling = RightChild(parent);
|
|
|
|
if (IsRed(sibling))
|
|
|
|
{
|
|
|
|
SetColor(sibling, BLACK);
|
|
|
|
SetColor(parent, RED);
|
|
|
|
RotateLeft(parent);
|
|
|
|
|
|
|
|
// We may have a new parent now
|
|
|
|
parent = Parent(elem);
|
|
|
|
sibling = RightChild(parent);
|
|
|
|
}
|
|
|
|
if ((IsBlack(LeftChild(sibling))) && (IsBlack(RightChild(sibling))))
|
|
|
|
{
|
|
|
|
if (sibling != InvalidIndex())
|
|
|
|
SetColor(sibling, RED);
|
|
|
|
elem = parent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (IsBlack(RightChild(sibling)))
|
|
|
|
{
|
|
|
|
SetColor(LeftChild(sibling), BLACK);
|
|
|
|
SetColor(sibling, RED);
|
|
|
|
RotateRight(sibling);
|
|
|
|
|
|
|
|
// rotation may have changed this
|
|
|
|
parent = Parent(elem);
|
|
|
|
sibling = RightChild(parent);
|
|
|
|
}
|
|
|
|
SetColor(sibling, Color(parent));
|
|
|
|
SetColor(parent, BLACK);
|
|
|
|
SetColor(RightChild(sibling), BLACK);
|
|
|
|
RotateLeft(parent);
|
|
|
|
elem = m_Root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Elem is the right child of the parent
|
|
|
|
I sibling = LeftChild(parent);
|
|
|
|
if (IsRed(sibling))
|
|
|
|
{
|
|
|
|
SetColor(sibling, BLACK);
|
|
|
|
SetColor(parent, RED);
|
|
|
|
RotateRight(parent);
|
|
|
|
|
|
|
|
// We may have a new parent now
|
|
|
|
parent = Parent(elem);
|
|
|
|
sibling = LeftChild(parent);
|
|
|
|
}
|
|
|
|
if ((IsBlack(RightChild(sibling))) && (IsBlack(LeftChild(sibling))))
|
|
|
|
{
|
|
|
|
if (sibling != InvalidIndex())
|
|
|
|
SetColor(sibling, RED);
|
|
|
|
elem = parent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (IsBlack(LeftChild(sibling)))
|
|
|
|
{
|
|
|
|
SetColor(RightChild(sibling), BLACK);
|
|
|
|
SetColor(sibling, RED);
|
|
|
|
RotateLeft(sibling);
|
|
|
|
|
|
|
|
// rotation may have changed this
|
|
|
|
parent = Parent(elem);
|
|
|
|
sibling = LeftChild(parent);
|
|
|
|
}
|
|
|
|
SetColor(sibling, Color(parent));
|
|
|
|
SetColor(parent, BLACK);
|
|
|
|
SetColor(LeftChild(sibling), BLACK);
|
|
|
|
RotateRight(parent);
|
|
|
|
elem = m_Root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SetColor(elem, BLACK);
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::Unlink(I elem)
|
|
|
|
{
|
|
|
|
if (elem != InvalidIndex())
|
|
|
|
{
|
|
|
|
I x, y;
|
|
|
|
|
|
|
|
if ((LeftChild(elem) == InvalidIndex()) ||
|
|
|
|
(RightChild(elem) == InvalidIndex()))
|
|
|
|
{
|
|
|
|
/* y has a NIL node as a child */
|
|
|
|
y = elem;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* find tree successor with a NIL node as a child */
|
|
|
|
y = RightChild(elem);
|
|
|
|
while (LeftChild(y) != InvalidIndex())
|
|
|
|
y = LeftChild(y);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* x is y's only child */
|
|
|
|
if (LeftChild(y) != InvalidIndex())
|
|
|
|
x = LeftChild(y);
|
|
|
|
else
|
|
|
|
x = RightChild(y);
|
|
|
|
|
|
|
|
/* remove y from the parent chain */
|
|
|
|
if (x != InvalidIndex())
|
|
|
|
SetParent(x, Parent(y));
|
|
|
|
if (!IsRoot(y))
|
|
|
|
{
|
|
|
|
if (IsLeftChild(y))
|
|
|
|
SetLeftChild(Parent(y), x);
|
|
|
|
else
|
|
|
|
SetRightChild(Parent(y), x);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_Root = x;
|
|
|
|
|
|
|
|
// need to store this off now, we'll be resetting y's color
|
|
|
|
NodeColor_t ycolor = Color(y);
|
|
|
|
if (y != elem)
|
|
|
|
{
|
|
|
|
// Standard implementations copy the data around, we cannot here.
|
|
|
|
// Hook in y to link to the same stuff elem used to.
|
|
|
|
SetParent(y, Parent(elem));
|
|
|
|
SetRightChild(y, RightChild(elem));
|
|
|
|
SetLeftChild(y, LeftChild(elem));
|
|
|
|
|
|
|
|
if (!IsRoot(elem))
|
|
|
|
if (IsLeftChild(elem))
|
|
|
|
SetLeftChild(Parent(elem), y);
|
|
|
|
else
|
|
|
|
SetRightChild(Parent(elem), y);
|
|
|
|
else
|
|
|
|
m_Root = y;
|
|
|
|
|
|
|
|
if (LeftChild(y) != InvalidIndex())
|
|
|
|
SetParent(LeftChild(y), y);
|
|
|
|
if (RightChild(y) != InvalidIndex())
|
|
|
|
SetParent(RightChild(y), y);
|
|
|
|
|
|
|
|
SetColor(y, Color(elem));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((x != InvalidIndex()) && (ycolor == BLACK))
|
|
|
|
RemoveRebalance(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::Link(I elem)
|
|
|
|
{
|
|
|
|
if (elem != InvalidIndex())
|
|
|
|
{
|
|
|
|
I parent = InvalidIndex();
|
|
|
|
bool leftchild = false;
|
|
|
|
|
|
|
|
FindInsertionPosition(Element(elem), parent, leftchild);
|
|
|
|
|
|
|
|
LinkToParent(elem, parent, leftchild);
|
|
|
|
|
|
|
|
Assert(IsValid());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Delete a node from the tree
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::RemoveAt(I elem)
|
|
|
|
{
|
|
|
|
if (elem != InvalidIndex())
|
|
|
|
{
|
|
|
|
Unlink(elem);
|
|
|
|
|
|
|
|
FreeNode(elem);
|
|
|
|
--m_NumElements;
|
|
|
|
|
|
|
|
Assert(IsValid());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// remove a node in the tree
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M > bool CUtlRBTree<T, I, L, M>::Remove(T const& search)
|
|
|
|
{
|
|
|
|
I node = Find(search);
|
|
|
|
if (node != InvalidIndex())
|
|
|
|
{
|
|
|
|
RemoveAt(node);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Removes all nodes from the tree
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::RemoveAll()
|
|
|
|
{
|
|
|
|
// Have to do some convoluted stuff to invoke the destructor on all
|
|
|
|
// valid elements for the multilist case (since we don't have all elements
|
|
|
|
// connected to each other in a list).
|
|
|
|
|
|
|
|
if (m_LastAlloc == m_Elements.InvalidIterator())
|
|
|
|
{
|
|
|
|
Assert(m_Root == InvalidIndex());
|
|
|
|
Assert(m_FirstFree == InvalidIndex());
|
|
|
|
Assert(m_NumElements == 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (typename M::Iterator_t it = m_Elements.First(); it != m_Elements.InvalidIterator(); it = m_Elements.Next(it))
|
|
|
|
{
|
|
|
|
I i = m_Elements.GetIndex(it);
|
|
|
|
if (IsValidIndex(i)) // skip elements in the free list
|
|
|
|
{
|
|
|
|
Destruct(&Element(i));
|
|
|
|
SetRightChild(i, m_FirstFree);
|
|
|
|
SetLeftChild(i, i);
|
|
|
|
m_FirstFree = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (it == m_LastAlloc)
|
|
|
|
break; // don't destruct elements that haven't ever been constucted
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear everything else out
|
|
|
|
m_Root = InvalidIndex();
|
|
|
|
m_NumElements = 0;
|
|
|
|
|
|
|
|
Assert(IsValid());
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Removes all nodes from the tree and purges memory
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::Purge()
|
|
|
|
{
|
|
|
|
RemoveAll();
|
|
|
|
m_FirstFree = InvalidIndex();
|
|
|
|
m_Elements.Purge();
|
|
|
|
m_LastAlloc = m_Elements.InvalidIterator();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// iteration
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::FirstInorder() const
|
|
|
|
{
|
|
|
|
I i = m_Root;
|
|
|
|
while (LeftChild(i) != InvalidIndex())
|
|
|
|
i = LeftChild(i);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::NextInorder(I i) const
|
|
|
|
{
|
|
|
|
Assert(IsValidIndex(i));
|
|
|
|
|
|
|
|
if (RightChild(i) != InvalidIndex())
|
|
|
|
{
|
|
|
|
i = RightChild(i);
|
|
|
|
while (LeftChild(i) != InvalidIndex())
|
|
|
|
i = LeftChild(i);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
I parent = Parent(i);
|
|
|
|
while (IsRightChild(i))
|
|
|
|
{
|
|
|
|
i = parent;
|
|
|
|
if (i == InvalidIndex()) break;
|
|
|
|
parent = Parent(i);
|
|
|
|
}
|
|
|
|
return parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::PrevInorder(I i) const
|
|
|
|
{
|
|
|
|
Assert(IsValidIndex(i));
|
|
|
|
|
|
|
|
if (LeftChild(i) != InvalidIndex())
|
|
|
|
{
|
|
|
|
i = LeftChild(i);
|
|
|
|
while (RightChild(i) != InvalidIndex())
|
|
|
|
i = RightChild(i);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
I parent = Parent(i);
|
|
|
|
while (IsLeftChild(i))
|
|
|
|
{
|
|
|
|
i = parent;
|
|
|
|
if (i == InvalidIndex()) break;
|
|
|
|
parent = Parent(i);
|
|
|
|
}
|
|
|
|
return parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::LastInorder() const
|
|
|
|
{
|
|
|
|
I i = m_Root;
|
|
|
|
while (RightChild(i) != InvalidIndex())
|
|
|
|
i = RightChild(i);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::FirstPreorder() const
|
|
|
|
{
|
|
|
|
return m_Root;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::NextPreorder(I i) const
|
|
|
|
{
|
|
|
|
if (LeftChild(i) != InvalidIndex())
|
|
|
|
return LeftChild(i);
|
|
|
|
|
|
|
|
if (RightChild(i) != InvalidIndex())
|
|
|
|
return RightChild(i);
|
|
|
|
|
|
|
|
I parent = Parent(i);
|
|
|
|
while (parent != InvalidIndex())
|
|
|
|
{
|
|
|
|
if (IsLeftChild(i) && (RightChild(parent) != InvalidIndex()))
|
|
|
|
return RightChild(parent);
|
|
|
|
i = parent;
|
|
|
|
parent = Parent(parent);
|
|
|
|
}
|
|
|
|
return InvalidIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::PrevPreorder(I i) const
|
|
|
|
{
|
|
|
|
Assert(0); // not implemented yet
|
|
|
|
return InvalidIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::LastPreorder() const
|
|
|
|
{
|
|
|
|
I i = m_Root;
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
while (RightChild(i) != InvalidIndex())
|
|
|
|
i = RightChild(i);
|
|
|
|
|
|
|
|
if (LeftChild(i) != InvalidIndex())
|
|
|
|
i = LeftChild(i);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::FirstPostorder() const
|
|
|
|
{
|
|
|
|
I i = m_Root;
|
|
|
|
while (!IsLeaf(i))
|
|
|
|
{
|
|
|
|
if (LeftChild(i))
|
|
|
|
i = LeftChild(i);
|
|
|
|
else
|
|
|
|
i = RightChild(i);
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::NextPostorder(I i) const
|
|
|
|
{
|
|
|
|
I parent = Parent(i);
|
|
|
|
if (parent == InvalidIndex())
|
|
|
|
return InvalidIndex();
|
|
|
|
|
|
|
|
if (IsRightChild(i))
|
|
|
|
return parent;
|
|
|
|
|
|
|
|
if (RightChild(parent) == InvalidIndex())
|
|
|
|
return parent;
|
|
|
|
|
|
|
|
i = RightChild(parent);
|
|
|
|
while (!IsLeaf(i))
|
|
|
|
{
|
|
|
|
if (LeftChild(i))
|
|
|
|
i = LeftChild(i);
|
|
|
|
else
|
|
|
|
i = RightChild(i);
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::Reinsert(I elem)
|
|
|
|
{
|
|
|
|
Unlink(elem);
|
|
|
|
Link(elem);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// returns the tree depth (not a very fast operation)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
int CUtlRBTree<T, I, L, M>::Depth(I node) const
|
|
|
|
{
|
|
|
|
if (node == InvalidIndex())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int depthright = Depth(RightChild(node));
|
|
|
|
int depthleft = Depth(LeftChild(node));
|
|
|
|
return MAX(depthright, depthleft) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//#define UTLTREE_PARANOID
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Makes sure the tree is valid after every operation
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
bool CUtlRBTree<T, I, L, M>::IsValid() const
|
|
|
|
{
|
|
|
|
if (!Count())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (m_LastAlloc == m_Elements.InvalidIterator())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!m_Elements.IsIdxValid(Root()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (Parent(Root()) != InvalidIndex())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
#ifdef UTLTREE_PARANOID
|
|
|
|
|
|
|
|
// First check to see that mNumEntries matches reality.
|
|
|
|
// count items on the free list
|
|
|
|
int numFree = 0;
|
|
|
|
for (int i = m_FirstFree; i != InvalidIndex(); i = RightChild(i))
|
|
|
|
{
|
|
|
|
++numFree;
|
|
|
|
if (!m_Elements.IsIdxValid(i))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// iterate over all elements, looking for validity
|
|
|
|
// based on the self pointers
|
|
|
|
int nElements = 0;
|
|
|
|
int numFree2 = 0;
|
|
|
|
for (M::Iterator_t it = m_Elements.First(); it != m_Elements.InvalidIterator(); it = m_Elements.Next(it))
|
|
|
|
{
|
|
|
|
I i = m_Elements.GetIndex(it);
|
|
|
|
if (!IsValidIndex(i))
|
|
|
|
{
|
|
|
|
++numFree2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++nElements;
|
|
|
|
|
|
|
|
int right = RightChild(i);
|
|
|
|
int left = LeftChild(i);
|
|
|
|
if ((right == left) && (right != InvalidIndex()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (right != InvalidIndex())
|
|
|
|
{
|
|
|
|
if (!IsValidIndex(right))
|
|
|
|
return false;
|
|
|
|
if (Parent(right) != i)
|
|
|
|
return false;
|
|
|
|
if (IsRed(i) && IsRed(right))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (left != InvalidIndex())
|
|
|
|
{
|
|
|
|
if (!IsValidIndex(left))
|
|
|
|
return false;
|
|
|
|
if (Parent(left) != i)
|
|
|
|
return false;
|
|
|
|
if (IsRed(i) && IsRed(left))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (it == m_LastAlloc)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (numFree2 != numFree)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (nElements != m_NumElements)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
#endif // UTLTREE_PARANOID
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Sets the less func
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::SetLessFunc(const typename CUtlRBTree<T, I, L, M>::LessFunc_t& func)
|
|
|
|
{
|
|
|
|
if (!m_LessFunc)
|
|
|
|
{
|
|
|
|
m_LessFunc = func;
|
|
|
|
}
|
|
|
|
else if (Count() > 0)
|
|
|
|
{
|
|
|
|
// need to re-sort the tree here....
|
|
|
|
Assert(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// inserts a node into the tree
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// Inserts a node into the tree, doesn't copy the data in.
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::FindInsertionPosition(T const& insert, I& parent, bool& leftchild)
|
|
|
|
{
|
|
|
|
Assert(!!m_LessFunc);
|
|
|
|
|
|
|
|
/* find where node belongs */
|
|
|
|
I current = m_Root;
|
|
|
|
parent = InvalidIndex();
|
|
|
|
leftchild = false;
|
|
|
|
while (current != InvalidIndex())
|
|
|
|
{
|
|
|
|
parent = current;
|
|
|
|
if (m_LessFunc(insert, Element(current)))
|
|
|
|
{
|
|
|
|
leftchild = true; current = LeftChild(current);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
leftchild = false; current = RightChild(current);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::Insert(T const& insert)
|
|
|
|
{
|
|
|
|
// use copy constructor to copy it in
|
|
|
|
I parent = InvalidIndex();
|
|
|
|
bool leftchild = false;
|
|
|
|
FindInsertionPosition(insert, parent, leftchild);
|
|
|
|
I newNode = InsertAt(parent, leftchild);
|
|
|
|
CopyConstruct(&Element(newNode), insert);
|
|
|
|
return newNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::Insert(const T* pArray, int nItems)
|
|
|
|
{
|
|
|
|
while (nItems--)
|
|
|
|
{
|
|
|
|
Insert(*pArray++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::InsertIfNotFound(T const& insert)
|
|
|
|
{
|
|
|
|
// use copy constructor to copy it in
|
|
|
|
I parent;
|
|
|
|
bool leftchild;
|
|
|
|
|
|
|
|
I current = m_Root;
|
|
|
|
parent = InvalidIndex();
|
|
|
|
leftchild = false;
|
|
|
|
while (current != InvalidIndex())
|
|
|
|
{
|
|
|
|
parent = current;
|
|
|
|
if (m_LessFunc(insert, Element(current)))
|
|
|
|
{
|
|
|
|
leftchild = true; current = LeftChild(current);
|
|
|
|
}
|
|
|
|
else if (m_LessFunc(Element(current), insert))
|
|
|
|
{
|
|
|
|
leftchild = false; current = RightChild(current);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
// Match found, no insertion
|
|
|
|
return InvalidIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
I newNode = InsertAt(parent, leftchild);
|
|
|
|
CopyConstruct(&Element(newNode), insert);
|
|
|
|
return newNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// finds a node in the tree
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
I CUtlRBTree<T, I, L, M>::Find(T const& search) const
|
|
|
|
{
|
|
|
|
Assert(!!m_LessFunc);
|
|
|
|
|
|
|
|
I current = m_Root;
|
|
|
|
while (current != InvalidIndex())
|
|
|
|
{
|
|
|
|
if (m_LessFunc(search, Element(current)))
|
|
|
|
current = LeftChild(current);
|
|
|
|
else if (m_LessFunc(Element(current), search))
|
|
|
|
current = RightChild(current);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// swap in place
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template < class T, class I, typename L, class M >
|
|
|
|
void CUtlRBTree<T, I, L, M>::Swap(CUtlRBTree< T, I, L >& that)
|
|
|
|
{
|
|
|
|
m_Elements.Swap(that.m_Elements);
|
|
|
|
V_swap(m_LessFunc, that.m_LessFunc);
|
|
|
|
V_swap(m_Root, that.m_Root);
|
|
|
|
V_swap(m_NumElements, that.m_NumElements);
|
|
|
|
V_swap(m_FirstFree, that.m_FirstFree);
|
|
|
|
V_swap(m_pElements, that.m_pElements);
|
|
|
|
V_swap(m_LastAlloc, that.m_LastAlloc);
|
|
|
|
Assert(IsValid());
|
|
|
|
Assert(m_Elements.IsValidIterator(m_LastAlloc) || (m_NumElements == 0 && m_FirstFree == InvalidIndex()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif // UTLRBTREE_H
|