| /** @file | |
| Priority Queue Implementation using Binary Heap. | |
| @section license License | |
| Licensed to the Apache Software Foundation (ASF) under one | |
| or more contributor license agreements. See the NOTICE file | |
| distributed with this work for additional information | |
| regarding copyright ownership. The ASF licenses this file | |
| to you under the Apache License, Version 2.0 (the | |
| "License"); you may not use this file except in compliance | |
| with the License. You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| */ | |
| #pragma once | |
| #include "tscore/ink_assert.h" | |
| #include <vector> | |
| #include <algorithm> | |
| template <typename T> struct PriorityQueueEntry { | |
| PriorityQueueEntry(T n) : node(n){}; | |
| PriorityQueueEntry() : node(NULL){}; | |
| uint32_t index = 0; | |
| T node; | |
| }; | |
| template <typename T> struct PriorityQueueLess { | |
| bool | |
| operator()(const T &a, const T &b) | |
| { | |
| return *a < *b; | |
| } | |
| }; | |
| template <typename T, class Comp = PriorityQueueLess<T>> class PriorityQueue | |
| { | |
| public: | |
| PriorityQueue() {} | |
| ~PriorityQueue() {} | |
| bool empty(); | |
| bool in(PriorityQueueEntry<T> *entry); | |
| PriorityQueueEntry<T> *top(); | |
| void pop(); | |
| void push(PriorityQueueEntry<T> *); | |
| void update(PriorityQueueEntry<T> *); | |
| void update(PriorityQueueEntry<T> *, bool); | |
| void erase(PriorityQueueEntry<T> *); | |
| const std::vector<PriorityQueueEntry<T> *> &dump() const; | |
| private: | |
| std::vector<PriorityQueueEntry<T> *> _v; | |
| void _swap(uint32_t, uint32_t); | |
| void _bubble_up(uint32_t); | |
| void _bubble_down(uint32_t); | |
| }; | |
| template <typename T, typename Comp> | |
| const std::vector<PriorityQueueEntry<T> *> & | |
| PriorityQueue<T, Comp>::dump() const | |
| { | |
| return _v; | |
| } | |
| template <typename T, typename Comp> | |
| bool | |
| PriorityQueue<T, Comp>::in(PriorityQueueEntry<T> *entry) | |
| { | |
| ink_release_assert(entry != nullptr); | |
| if (std::find(_v.begin(), _v.end(), entry) != _v.end()) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| template <typename T, typename Comp> | |
| bool | |
| PriorityQueue<T, Comp>::empty() | |
| { | |
| return _v.empty(); | |
| } | |
| template <typename T, typename Comp> | |
| void | |
| PriorityQueue<T, Comp>::push(PriorityQueueEntry<T> *entry) | |
| { | |
| ink_release_assert(entry != nullptr); | |
| int len = _v.size(); | |
| _v.push_back(entry); | |
| entry->index = len; | |
| _bubble_up(len); | |
| } | |
| template <typename T, typename Comp> | |
| PriorityQueueEntry<T> * | |
| PriorityQueue<T, Comp>::top() | |
| { | |
| if (empty()) { | |
| return nullptr; | |
| } else { | |
| return _v[0]; | |
| } | |
| } | |
| template <typename T, typename Comp> | |
| void | |
| PriorityQueue<T, Comp>::pop() | |
| { | |
| if (empty()) { | |
| return; | |
| } | |
| const uint32_t original_index = _v[0]->index; | |
| _swap(0, _v.size() - 1); | |
| _v[_v.size() - 1]->index = original_index; | |
| _v.pop_back(); | |
| _bubble_down(0); | |
| } | |
| template <typename T, typename Comp> | |
| void | |
| PriorityQueue<T, Comp>::erase(PriorityQueueEntry<T> *entry) | |
| { | |
| if (empty()) { | |
| return; | |
| } | |
| // If the entry doesn't belong to this queue just return. | |
| if (entry != _v[entry->index]) { | |
| ink_assert(!in(entry)); | |
| return; | |
| } | |
| ink_release_assert(entry->index < _v.size()); | |
| const uint32_t original_index = entry->index; | |
| if (original_index != (_v.size() - 1)) { | |
| // Move the erased item to the end to be popped off | |
| _swap(original_index, _v.size() - 1); | |
| // Fix the index before we pop it | |
| _v[_v.size() - 1]->index = original_index; | |
| _v.pop_back(); | |
| _bubble_down(original_index); | |
| _bubble_up(original_index); | |
| } else { // Otherwise, we are already at the end, just pop | |
| _v.pop_back(); | |
| } | |
| } | |
| template <typename T, typename Comp> | |
| void | |
| PriorityQueue<T, Comp>::update(PriorityQueueEntry<T> *entry) | |
| { | |
| ink_release_assert(entry != nullptr); | |
| if (empty()) { | |
| return; | |
| } | |
| // One of them will works whether weight is increased or decreased | |
| _bubble_down(entry->index); | |
| _bubble_up(entry->index); | |
| } | |
| template <typename T, typename Comp> | |
| void | |
| PriorityQueue<T, Comp>::update(PriorityQueueEntry<T> *entry, bool increased) | |
| { | |
| ink_release_assert(entry != nullptr); | |
| if (empty()) { | |
| return; | |
| } | |
| if (increased) { | |
| _bubble_down(entry->index); | |
| } else { | |
| _bubble_up(entry->index); | |
| } | |
| } | |
| template <typename T, typename Comp> | |
| void | |
| PriorityQueue<T, Comp>::_swap(uint32_t i, uint32_t j) | |
| { | |
| PriorityQueueEntry<T> *tmp = _v[i]; | |
| _v[i] = _v[j]; | |
| _v[j] = tmp; | |
| _v[i]->index = i; | |
| _v[j]->index = j; | |
| } | |
| template <typename T, typename Comp> | |
| void | |
| PriorityQueue<T, Comp>::_bubble_up(uint32_t index) | |
| { | |
| if (empty()) { | |
| ink_release_assert(false); | |
| } | |
| Comp comp; | |
| uint32_t parent; | |
| while (index != 0) { | |
| parent = (index - 1) / 2; | |
| if (comp(_v[index]->node, _v[parent]->node)) { | |
| _swap(parent, index); | |
| index = parent; | |
| continue; | |
| } | |
| break; | |
| } | |
| } | |
| template <typename T, typename Comp> | |
| void | |
| PriorityQueue<T, Comp>::_bubble_down(uint32_t index) | |
| { | |
| if (empty()) { | |
| // Do nothing | |
| return; | |
| } | |
| uint32_t left, right, smaller; | |
| Comp comp; | |
| while (true) { | |
| if ((left = index * 2 + 1) >= _v.size()) { | |
| break; | |
| } else if ((right = index * 2 + 2) >= _v.size()) { | |
| smaller = left; | |
| } else { | |
| smaller = comp(_v[left]->node, _v[right]->node) ? left : right; | |
| } | |
| if (comp(_v[smaller]->node, _v[index]->node)) { | |
| _swap(smaller, index); | |
| index = smaller; | |
| continue; | |
| } | |
| break; | |
| } | |
| } |