Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
3.8 KiB

  1. //========= Copyright � Valve Corporation, All rights reserved. ============//
  2. #ifndef MATHLIB_DISJOINT_SET_FOREST_HDR
  3. #define MATHLIB_DISJOINT_SET_FOREST_HDR
  4. #include "tier1/utlvector.h"
  5. /// An excellent overview of the concept is here:
  6. /// http://en.wikipedia.org/wiki/Disjoint-set_data_structure this algorithm is with path
  7. /// compression and ranking implemented, so it's essentially amortized const-time operations to
  8. /// find node's island representative element or union two lists. ( the "essentially" means
  9. /// amortized complexity is Ackermann function, which is like 5 for the largest number in any kind
  10. /// of software development )
  11. class CDisjointSetForest
  12. {
  13. public:
  14. CDisjointSetForest( int nCount );
  15. //void Flatten();
  16. int Find( int nNode );
  17. void Union( int nNodeA, int nNodeB );
  18. void EnsureExists( int nNode );
  19. int GetNodeCount()const { return m_Nodes.Count(); }
  20. protected:
  21. struct Node_t
  22. {
  23. int nRank, nParent;
  24. };
  25. CUtlVector< Node_t > m_Nodes;
  26. };
  27. inline CDisjointSetForest::CDisjointSetForest( int nCount )
  28. {
  29. m_Nodes.SetCount( nCount );
  30. for( int i = 0;i < nCount; ++i )
  31. {
  32. m_Nodes[i].nRank = 0;
  33. m_Nodes[i].nParent = i;
  34. }
  35. }
  36. inline void CDisjointSetForest::EnsureExists( int nNode )
  37. {
  38. int nOldCount = m_Nodes.Count();
  39. if ( nNode >= nOldCount )
  40. {
  41. m_Nodes.SetCountNonDestructively( nNode + 1 );
  42. for ( int n = nOldCount; n <= nNode; ++n )
  43. {
  44. m_Nodes[ n ].nRank = 0;
  45. m_Nodes[ n ].nParent = n;
  46. }
  47. }
  48. }
  49. /// Find the representative element for the node in graph representative element is the same for
  50. /// all connected nodes(vertices) in the graph, and it's one of the nodes in the connected set this
  51. /// implementation is without recursion to be more cache friendly; recursive implementation would
  52. /// be clearer, but this is simple enough
  53. inline int CDisjointSetForest::Find( int nStartNode )
  54. {
  55. int nTopParent;
  56. for( int nNode = nStartNode; nTopParent = m_Nodes[nNode].nParent, nNode != nTopParent ; )
  57. {
  58. nNode = nTopParent;
  59. }
  60. // found the top parent, now compress the path to achieve that amazing amortized acceleration
  61. int nParent;
  62. for( int nNode = nStartNode; nParent = m_Nodes[nNode].nParent, nNode != nParent ; )
  63. {
  64. m_Nodes[nNode].nParent = nTopParent;
  65. nNode = nParent;
  66. }
  67. Assert( nParent == nTopParent );
  68. return nTopParent;
  69. }
  70. /// Connect the two (potentially disjoint) sets
  71. inline void CDisjointSetForest::Union( int nNodeA, int nNodeB )
  72. {
  73. int nRootA = Find( nNodeA );
  74. int nRootB = Find( nNodeB );
  75. if ( m_Nodes[nRootA].nRank > m_Nodes[nRootB].nRank )
  76. {
  77. m_Nodes[nRootB].nParent = nRootA; // note: no change in rank! we're balanced!
  78. }
  79. else
  80. if ( m_Nodes[nRootA].nRank < m_Nodes[nRootB].nRank )
  81. {
  82. m_Nodes[nRootA].nParent = nRootB; // note: no change in rank! we're balanced!
  83. }
  84. else
  85. if ( nRootA != nRootB ) // Unless A and B are already in same set, merge them
  86. {
  87. m_Nodes[nRootB].nParent = nRootA;
  88. m_Nodes[nRootA].nRank = m_Nodes[nRootA].nRank + 1;
  89. }
  90. }
  91. /// Given the graph implementing GetParent(), find the indices of all children of the given tip of
  92. /// the subtree
  93. template <typename Graph_t, class BitVec_t>
  94. inline void ComputeSubtree( const Graph_t *pGraph, int nSubtreeTipBone, BitVec_t *pSubtree )
  95. {
  96. int nBoneCount = pSubtree->GetNumBits();
  97. Assert( nSubtreeTipBone >= 0 && nSubtreeTipBone < nBoneCount );
  98. CDisjointSetForest find( nBoneCount );
  99. for( int nBone = 0; nBone < nBoneCount; ++nBone )
  100. {
  101. if( nBone != nSubtreeTipBone ) // Important: severe the link between the subtree tip bone and the rest of the tree to find the disjoint subtree
  102. {
  103. int nParent = pGraph->GetParent( nBone );
  104. if( nParent >= 0 && nParent < nBoneCount )
  105. {
  106. find.Union( nBone, nParent );
  107. }
  108. }
  109. }
  110. int nIsland = find.Find( nSubtreeTipBone );
  111. for( int nBone = 0; nBone < nBoneCount; ++nBone )
  112. {
  113. if( find.Find( nBone ) == nIsland )
  114. {
  115. pSubtree->Set( nBone );
  116. }
  117. }
  118. }
  119. #endif //MATHLIB_DISJOINT_SET_FOREST_HDR