/* * Copyright 2013 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Paweł Dziepak, pdziepak@quarnos.org */ #ifndef KERNEL_UTIL_MIN_MAX_HEAP_H #define KERNEL_UTIL_MIN_MAX_HEAP_H #include #include template struct MinMaxHeapLink { MinMaxHeapLink(); bool fMinTree; int fIndex; Key fKey; }; template class MinMaxHeapLinkImpl { private: typedef MinMaxHeapLink Link; public: inline Link* GetMinMaxHeapLink(); private: Link fMinMaxHeapLink; }; template class MinMaxHeapStandardGetLink { private: typedef MinMaxHeapLink Link; public: inline Link* operator()(Element* element) const; }; template Element::*LinkMember> class MinMaxHeapMemberGetLink { private: typedef MinMaxHeapLink Link; public: inline Link* operator()(Element* element) const; }; template class MinMaxHeapCompare { public: inline bool operator()(Key a, Key b); }; #define MIN_MAX_HEAP_TEMPLATE_LIST \ template #define MIN_MAX_HEAP_CLASS_NAME MinMaxHeap template, typename GetLink = MinMaxHeapStandardGetLink > class MinMaxHeap { public: MinMaxHeap(); MinMaxHeap(int initialSize); ~MinMaxHeap(); inline Element* PeekMinimum() const; inline Element* PeekMaximum() const; static const Key& GetKey(Element* element); inline void ModifyKey(Element* element, Key newKey); inline void RemoveMinimum(); inline void RemoveMaximum(); inline status_t Insert(Element* element, Key key); private: status_t _GrowHeap(int minimalSize = 0); void _MoveUp(MinMaxHeapLink* link); void _MoveDown(MinMaxHeapLink* link); bool _ChangeTree(MinMaxHeapLink* link); void _RemoveLast(bool minTree); Element** fMinElements; int fMinLastElement; Element** fMaxElements; int fMaxLastElement; int fSize; static Compare sCompare; static GetLink sGetLink; }; #if KDEBUG template MinMaxHeapLink::MinMaxHeapLink() : fIndex(-1) { } #else template MinMaxHeapLink::MinMaxHeapLink() { } #endif template MinMaxHeapLink* MinMaxHeapLinkImpl::GetMinMaxHeapLink() { return &fMinMaxHeapLink; } template MinMaxHeapLink* MinMaxHeapStandardGetLink::operator()(Element* element) const { return element->GetMinMaxHeapLink(); } template Element::*LinkMember> MinMaxHeapLink* MinMaxHeapMemberGetLink::operator()( Element* element) const { return &(element->*LinkMember); } template bool MinMaxHeapCompare::operator()(Key a, Key b) { return a < b; } MIN_MAX_HEAP_TEMPLATE_LIST MIN_MAX_HEAP_CLASS_NAME::MinMaxHeap() : fMinElements(NULL), fMinLastElement(0), fMaxElements(NULL), fMaxLastElement(0), fSize(0) { } MIN_MAX_HEAP_TEMPLATE_LIST MIN_MAX_HEAP_CLASS_NAME::MinMaxHeap(int initialSize) : fMinElements(NULL), fMinLastElement(0), fMaxElements(NULL), fMaxLastElement(0), fSize(0) { _GrowHeap(initialSize); } MIN_MAX_HEAP_TEMPLATE_LIST MIN_MAX_HEAP_CLASS_NAME::~MinMaxHeap() { free(fMinElements); } MIN_MAX_HEAP_TEMPLATE_LIST Element* MIN_MAX_HEAP_CLASS_NAME::PeekMinimum() const { if (fMinLastElement > 0) return fMinElements[0]; else if (fMaxLastElement > 0) { ASSERT(fMaxLastElement == 1); return fMaxElements[0]; } return NULL; } MIN_MAX_HEAP_TEMPLATE_LIST Element* MIN_MAX_HEAP_CLASS_NAME::PeekMaximum() const { if (fMaxLastElement > 0) return fMaxElements[0]; else if (fMinLastElement > 0) { ASSERT(fMinLastElement == 1); return fMinElements[0]; } return NULL; } MIN_MAX_HEAP_TEMPLATE_LIST const Key& MIN_MAX_HEAP_CLASS_NAME::GetKey(Element* element) { return sGetLink(element)->fKey; } MIN_MAX_HEAP_TEMPLATE_LIST void MIN_MAX_HEAP_CLASS_NAME::ModifyKey(Element* element, Key newKey) { MinMaxHeapLink* link = sGetLink(element); Key oldKey = link->fKey; link->fKey = newKey; if (!sCompare(newKey, oldKey) && !sCompare(oldKey, newKey)) return; if (sCompare(newKey, oldKey) ^ !link->fMinTree) _MoveUp(link); else _MoveDown(link); } MIN_MAX_HEAP_TEMPLATE_LIST void MIN_MAX_HEAP_CLASS_NAME::RemoveMinimum() { if (fMinLastElement == 0) { ASSERT(fMaxLastElement == 1); RemoveMaximum(); return; } #if KDEBUG Element* element = PeekMinimum(); MinMaxHeapLink* link = sGetLink(element); ASSERT(link->fIndex != -1); link->fIndex = -1; #endif _RemoveLast(true); } MIN_MAX_HEAP_TEMPLATE_LIST void MIN_MAX_HEAP_CLASS_NAME::RemoveMaximum() { if (fMaxLastElement == 0) { ASSERT(fMinLastElement == 1); RemoveMinimum(); return; } #if KDEBUG Element* element = PeekMaximum(); MinMaxHeapLink* link = sGetLink(element); ASSERT(link->fIndex != -1); link->fIndex = -1; #endif _RemoveLast(false); } MIN_MAX_HEAP_TEMPLATE_LIST status_t MIN_MAX_HEAP_CLASS_NAME::Insert(Element* element, Key key) { if (min_c(fMinLastElement, fMaxLastElement) == fSize) { ASSERT(max_c(fMinLastElement, fMaxLastElement) == fSize); status_t result = _GrowHeap(); if (result != B_OK) return result; } ASSERT(fMinLastElement < fSize || fMaxLastElement < fSize); MinMaxHeapLink* link = sGetLink(element); ASSERT(link->fIndex == -1); link->fMinTree = fMinLastElement < fMaxLastElement; int& lastElement = link->fMinTree ? fMinLastElement : fMaxLastElement; Element** tree = link->fMinTree ? fMinElements : fMaxElements; tree[lastElement] = element; link->fIndex = lastElement++; link->fKey = key; if (!_ChangeTree(link)) _MoveUp(link); return B_OK; } MIN_MAX_HEAP_TEMPLATE_LIST status_t MIN_MAX_HEAP_CLASS_NAME::_GrowHeap(int minimalSize) { minimalSize = minimalSize % 2 == 0 ? minimalSize : minimalSize + 1; int newSize = max_c(max_c(fSize * 4, 4), minimalSize); size_t arraySize = newSize * sizeof(Element*); Element** newBuffer = reinterpret_cast(realloc(fMinElements, arraySize)); if (newBuffer == NULL) return B_NO_MEMORY; fMinElements = newBuffer; newBuffer += newSize / 2; if (fMaxLastElement > 0) memcpy(newBuffer, fMinElements + fSize, fSize * sizeof(Element*)); fMaxElements = newBuffer; fSize = newSize / 2; return B_OK; } MIN_MAX_HEAP_TEMPLATE_LIST void MIN_MAX_HEAP_CLASS_NAME::_MoveUp(MinMaxHeapLink* link) { Element** tree = link->fMinTree ? fMinElements : fMaxElements; while (true) { if (link->fIndex <= 0) break; int parent = (link->fIndex - 1) / 2; bool isSmaller = sCompare(link->fKey, sGetLink(tree[parent])->fKey); if (isSmaller ^ !link->fMinTree) { ASSERT(sGetLink(tree[parent])->fIndex == parent); sGetLink(tree[parent])->fIndex = link->fIndex; Element* element = tree[link->fIndex]; tree[link->fIndex] = tree[parent]; tree[parent] = element; link->fIndex = parent; } else break; } } MIN_MAX_HEAP_TEMPLATE_LIST void MIN_MAX_HEAP_CLASS_NAME::_MoveDown(MinMaxHeapLink* link) { int current; int lastElement = link->fMinTree ? fMinLastElement : fMaxLastElement; Element** tree = link->fMinTree ? fMinElements : fMaxElements; while (true) { current = link->fIndex; int child = 2 * link->fIndex + 1; if (child < lastElement) { bool isSmaller = sCompare(sGetLink(tree[child])->fKey, link->fKey); if (isSmaller ^ !link->fMinTree) current = child; } child = 2 * link->fIndex + 2; if (child < lastElement) { bool isSmaller = sCompare(sGetLink(tree[child])->fKey, sGetLink(tree[current])->fKey); if (isSmaller ^ !link->fMinTree) current = child; } if (link->fIndex == current) break; ASSERT(sGetLink(tree[current])->fIndex == current); sGetLink(tree[current])->fIndex = link->fIndex; Element* element = tree[link->fIndex]; tree[link->fIndex] = tree[current]; tree[current] = element; link->fIndex = current; } if (2 * link->fIndex + 1 >= lastElement) _ChangeTree(link); } MIN_MAX_HEAP_TEMPLATE_LIST bool MIN_MAX_HEAP_CLASS_NAME::_ChangeTree(MinMaxHeapLink* link) { int otherLastElement = link->fMinTree ? fMaxLastElement : fMinLastElement; Element** currentTree = link->fMinTree ? fMinElements : fMaxElements; Element** otherTree = link->fMinTree ? fMaxElements : fMinElements; if (otherLastElement <= 0) { ASSERT(link->fMinTree ? fMinLastElement : fMaxLastElement == 1); return false; } ASSERT((link->fIndex - 1) / 2 < otherLastElement); Element* predecessor; if (2 * link->fIndex + 1 < otherLastElement) { predecessor = otherTree[2 * link->fIndex + 1]; ASSERT(sGetLink(predecessor)->fIndex == 2 * link->fIndex + 1); } else if (link->fIndex < otherLastElement) { predecessor = otherTree[link->fIndex]; ASSERT(sGetLink(predecessor)->fIndex == link->fIndex); } else { predecessor = otherTree[(link->fIndex - 1) / 2]; ASSERT(sGetLink(predecessor)->fIndex == (link->fIndex - 1) / 2); } MinMaxHeapLink* predecessorLink = sGetLink(predecessor); bool isSmaller = sCompare(predecessorLink->fKey, link->fKey); if (isSmaller ^ !link->fMinTree) { Element* element = currentTree[link->fIndex]; currentTree[link->fIndex] = otherTree[predecessorLink->fIndex]; otherTree[predecessorLink->fIndex] = element; int index = link->fIndex; link->fIndex = predecessorLink->fIndex; predecessorLink->fIndex = index; predecessorLink->fMinTree = !predecessorLink->fMinTree; link->fMinTree = !link->fMinTree; _MoveUp(link); return true; } return false; } MIN_MAX_HEAP_TEMPLATE_LIST void MIN_MAX_HEAP_CLASS_NAME::_RemoveLast(bool minTree) { bool deleteMin = fMaxLastElement < fMinLastElement; Element** tree = deleteMin ? fMinElements : fMaxElements; int& lastElement = deleteMin ? fMinLastElement : fMaxLastElement; ASSERT(lastElement > 0); lastElement--; if (lastElement == 0 && deleteMin == minTree) return; Element* element = tree[lastElement]; if (minTree) fMinElements[0] = element; else fMaxElements[0] = element; MinMaxHeapLink* link = sGetLink(element); link->fIndex = 0; link->fMinTree = minTree; _MoveDown(link); } MIN_MAX_HEAP_TEMPLATE_LIST Compare MIN_MAX_HEAP_CLASS_NAME::sCompare; MIN_MAX_HEAP_TEMPLATE_LIST GetLink MIN_MAX_HEAP_CLASS_NAME::sGetLink; #endif // KERNEL_UTIL_MIN_MAX_HEAP_H