SplayTree.h (7636B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /** 8 * A sorted tree with optimal access times, where recently-accessed elements 9 * are faster to access again. 10 */ 11 12 #ifndef mozilla_SplayTree_h 13 #define mozilla_SplayTree_h 14 15 #include "mozilla/Assertions.h" 16 17 namespace mozilla { 18 19 template <class T, class C> 20 class SplayTree; 21 22 template <typename T> 23 class SplayTreeNode { 24 public: 25 template <class A, class B> 26 friend class SplayTree; 27 28 SplayTreeNode() : mLeft(nullptr), mRight(nullptr), mParent(nullptr) {} 29 30 private: 31 T* mLeft; 32 T* mRight; 33 T* mParent; 34 }; 35 36 /** 37 * Class which represents a splay tree. 38 * Splay trees are balanced binary search trees for which search, insert and 39 * remove are all amortized O(log n), but where accessing a node makes it 40 * faster to access that node in the future. 41 * 42 * T indicates the type of tree elements, Comparator must have a static 43 * compare(const T&, const T&) method ordering the elements. The compare 44 * method must be free from side effects. 45 */ 46 template <typename T, class Comparator> 47 class SplayTree { 48 T* mRoot; 49 50 public: 51 constexpr SplayTree() : mRoot(nullptr) {} 52 53 bool empty() const { return !mRoot; } 54 55 T* find(const T& aValue) { 56 if (empty()) { 57 return nullptr; 58 } 59 60 T* last = lookup(aValue); 61 splay(last); 62 return Comparator::compare(aValue, *last) == 0 ? last : nullptr; 63 } 64 65 void insert(T* aValue) { 66 MOZ_ASSERT(!find(*aValue), "Duplicate elements are not allowed."); 67 68 if (!mRoot) { 69 mRoot = aValue; 70 return; 71 } 72 T* last = lookup(*aValue); 73 int cmp = Comparator::compare(*aValue, *last); 74 75 finishInsertion(last, cmp, aValue); 76 } 77 78 T* findOrInsert(const T& aValue); 79 80 T* remove(const T& aValue) { 81 T* last = lookup(aValue); 82 MOZ_ASSERT(last, "This tree must contain the element being removed."); 83 MOZ_ASSERT(Comparator::compare(aValue, *last) == 0); 84 85 // Splay the tree so that the item to remove is the root. 86 splay(last); 87 MOZ_ASSERT(last == mRoot); 88 89 // Find another node which can be swapped in for the root: either the 90 // rightmost child of the root's left, or the leftmost child of the 91 // root's right. 92 T* swap; 93 T* swapChild; 94 if (mRoot->mLeft) { 95 swap = mRoot->mLeft; 96 while (swap->mRight) { 97 swap = swap->mRight; 98 } 99 swapChild = swap->mLeft; 100 } else if (mRoot->mRight) { 101 swap = mRoot->mRight; 102 while (swap->mLeft) { 103 swap = swap->mLeft; 104 } 105 swapChild = swap->mRight; 106 } else { 107 T* result = mRoot; 108 mRoot = nullptr; 109 return result; 110 } 111 112 // The selected node has at most one child, in swapChild. Detach it 113 // from the subtree by replacing it with that child. 114 if (swap == swap->mParent->mLeft) { 115 swap->mParent->mLeft = swapChild; 116 } else { 117 swap->mParent->mRight = swapChild; 118 } 119 if (swapChild) { 120 swapChild->mParent = swap->mParent; 121 } 122 123 // Make the selected node the new root. 124 mRoot = swap; 125 mRoot->mParent = nullptr; 126 mRoot->mLeft = last->mLeft; 127 mRoot->mRight = last->mRight; 128 if (mRoot->mLeft) { 129 mRoot->mLeft->mParent = mRoot; 130 } 131 if (mRoot->mRight) { 132 mRoot->mRight->mParent = mRoot; 133 } 134 135 last->mLeft = nullptr; 136 last->mRight = nullptr; 137 return last; 138 } 139 140 T* removeMin() { 141 MOZ_ASSERT(mRoot, "No min to remove!"); 142 143 T* min = mRoot; 144 while (min->mLeft) { 145 min = min->mLeft; 146 } 147 return remove(*min); 148 } 149 150 // For testing purposes only. 151 void checkCoherency() { checkCoherency(mRoot, nullptr); } 152 153 private: 154 /** 155 * Returns the node in this comparing equal to |aValue|, or a node just 156 * greater or just less than |aValue| if there is no such node. 157 */ 158 T* lookup(const T& aValue) { 159 MOZ_ASSERT(!empty()); 160 161 T* node = mRoot; 162 T* parent; 163 do { 164 parent = node; 165 int c = Comparator::compare(aValue, *node); 166 if (c == 0) { 167 return node; 168 } else if (c < 0) { 169 node = node->mLeft; 170 } else { 171 node = node->mRight; 172 } 173 } while (node); 174 return parent; 175 } 176 177 void finishInsertion(T* aLast, int32_t aCmp, T* aNew) { 178 MOZ_ASSERT(aCmp, "Nodes shouldn't be equal!"); 179 180 T** parentPointer = (aCmp < 0) ? &aLast->mLeft : &aLast->mRight; 181 MOZ_ASSERT(!*parentPointer); 182 *parentPointer = aNew; 183 aNew->mParent = aLast; 184 185 splay(aNew); 186 } 187 188 /** 189 * Rotate the tree until |node| is at the root of the tree. Performing 190 * the rotations in this fashion preserves the amortized balancing of 191 * the tree. 192 */ 193 void splay(T* aNode) { 194 MOZ_ASSERT(aNode); 195 196 while (aNode != mRoot) { 197 T* parent = aNode->mParent; 198 if (parent == mRoot) { 199 // Zig rotation. 200 rotate(aNode); 201 MOZ_ASSERT(aNode == mRoot); 202 return; 203 } 204 T* grandparent = parent->mParent; 205 if ((parent->mLeft == aNode) == (grandparent->mLeft == parent)) { 206 // Zig-zig rotation. 207 rotate(parent); 208 rotate(aNode); 209 } else { 210 // Zig-zag rotation. 211 rotate(aNode); 212 rotate(aNode); 213 } 214 } 215 } 216 217 void rotate(T* aNode) { 218 // Rearrange nodes so that aNode becomes the parent of its current 219 // parent, while preserving the sortedness of the tree. 220 T* parent = aNode->mParent; 221 if (parent->mLeft == aNode) { 222 // x y 223 // y c ==> a x 224 // a b b c 225 parent->mLeft = aNode->mRight; 226 if (aNode->mRight) { 227 aNode->mRight->mParent = parent; 228 } 229 aNode->mRight = parent; 230 } else { 231 MOZ_ASSERT(parent->mRight == aNode); 232 // x y 233 // a y ==> x c 234 // b c a b 235 parent->mRight = aNode->mLeft; 236 if (aNode->mLeft) { 237 aNode->mLeft->mParent = parent; 238 } 239 aNode->mLeft = parent; 240 } 241 aNode->mParent = parent->mParent; 242 parent->mParent = aNode; 243 if (T* grandparent = aNode->mParent) { 244 if (grandparent->mLeft == parent) { 245 grandparent->mLeft = aNode; 246 } else { 247 grandparent->mRight = aNode; 248 } 249 } else { 250 mRoot = aNode; 251 } 252 } 253 254 T* checkCoherency(T* aNode, T* aMinimum) { 255 if (mRoot) { 256 MOZ_RELEASE_ASSERT(!mRoot->mParent); 257 } 258 if (!aNode) { 259 MOZ_RELEASE_ASSERT(!mRoot); 260 return nullptr; 261 } 262 if (!aNode->mParent) { 263 MOZ_RELEASE_ASSERT(aNode == mRoot); 264 } 265 if (aMinimum) { 266 MOZ_RELEASE_ASSERT(Comparator::compare(*aMinimum, *aNode) < 0); 267 } 268 if (aNode->mLeft) { 269 MOZ_RELEASE_ASSERT(aNode->mLeft->mParent == aNode); 270 T* leftMaximum = checkCoherency(aNode->mLeft, aMinimum); 271 MOZ_RELEASE_ASSERT(Comparator::compare(*leftMaximum, *aNode) < 0); 272 } 273 if (aNode->mRight) { 274 MOZ_RELEASE_ASSERT(aNode->mRight->mParent == aNode); 275 return checkCoherency(aNode->mRight, aNode); 276 } 277 return aNode; 278 } 279 280 SplayTree(const SplayTree&) = delete; 281 void operator=(const SplayTree&) = delete; 282 }; 283 284 template <typename T, class Comparator> 285 T* SplayTree<T, Comparator>::findOrInsert(const T& aValue) { 286 if (!mRoot) { 287 mRoot = new T(aValue); 288 return mRoot; 289 } 290 291 T* last = lookup(aValue); 292 int cmp = Comparator::compare(aValue, *last); 293 if (!cmp) { 294 return last; 295 } 296 297 T* t = new T(aValue); 298 finishInsertion(last, cmp, t); 299 return t; 300 } 301 302 } /* namespace mozilla */ 303 304 #endif /* mozilla_SplayTree_h */