blob: 10b17a4375baf507b41046cf8a113c797b2b174f [file] [log] [blame]
// 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 "codegen/impala-ir.h"
namespace impala {
template <typename T, typename C>
class PriorityQueue;
// A simple iterator for PriorityQueue
template <typename T, typename C>
class PriorityQueueIterator {
public:
PriorityQueueIterator(PriorityQueue<T, C>* queue, int index)
: queue_(queue), index_(index) {}
~PriorityQueueIterator() {}
PriorityQueue<T, C>* GetPriorityQueue() { return queue_; }
// The operator to cast 'this' to int
operator int() { return index_; }
// The dereference operator
T& operator*() { return queue_->elements_[index_]; }
// The prefix increment operator ++itor
PriorityQueueIterator<T, C>& operator++() {
index_++;
return *this;
}
// The Less operator
bool operator<(PriorityQueueIterator& other) { return index_ < other.index_; }
private:
// The priority queue.
PriorityQueue<T, C>* queue_;
// The index to the element that this iterator is pointing at.
int index_;
};
// A priority queue template based on the array implementation of a binary search tree.
// The first type T is the type of the elements of the queue while the second type C
// is the comparator to compare elements during the heapification operations.
//
// Within the template, the array elements_ (represented by std::vector<T>) holds the
// elements of the queue.
//
// This class calls C.Less(const T& x, const T& y) to compare elements and expects a
// 'true' return when x < y. Since any node in the binary search tree is larger than
// any of its two children, this class implements a max heap.
//
// The grow and shrink of the array is maintained automatically by std::vector<T> and
// the array is destroyed upon the destruction of the queue.
//
// To add to the queue, call Push(). To remove the top element, call Pop(). To look at
// the top element without removal, call Top().
//
// The priority queue is not thread safe.
//
template <typename T, typename C>
class PriorityQueue {
public:
PriorityQueue(const C& c) : elements_(), comparator_(c) {}
~PriorityQueue() {}
// Clear all elements
void Clear() { elements_.clear(); }
// The top element
T& IR_ALWAYS_INLINE Top() { return elements_[0]; }
// The ith element
T& operator[](int i) { return elements_[i]; }
void Reserve(int64_t capacity) { elements_.reserve(capacity); }
// The size f the heap
int64_t IR_ALWAYS_INLINE Size() const { return elements_.size(); }
// Check if the heap is empty
bool IR_ALWAYS_INLINE Empty() const { return elements_.empty(); }
// Get the begin iterator.
PriorityQueueIterator<T, C> Begin() { return PriorityQueueIterator<T, C>(this, 0); }
// Get the end iterator.
PriorityQueueIterator<T, C> End() { return PriorityQueueIterator<T, C>(this, Size()); }
// Add a new element to the end of the array and then heapify. This is an
// O(log n) operation.
void IR_ALWAYS_INLINE Push(const T& v) {
// Insert the new element v at the end.
elements_.emplace_back(v);
// Move up the new element and its ancestors if necessary (all the way to the
// element at index 0) to maintain the heap property.
HeapifySubtreeUp(0, Size() - 1);
}
// Remove the top element from the priority queue and then heapify affected
// elements. This is an O(log n) operation. Must not be called when the queue
// is empty.
T IR_ALWAYS_INLINE Pop() {
if (Size() > 0) {
int last = Size() - 1;
T top_element = Top();
// Swap the top element from 0 to index 'last'.
Swap(elements_[0], elements_[last]);
// Heapify from index 0 and down.
HeapifySubtreeDown(0, last);
elements_.pop_back();
// Return the original top element.
return top_element;
}
DCHECK(false);
return T();
}
// Heapify from top element at index 0 in O(log n) complexity. It is
// assumed that the elements in [1, size_-1] are already arranged as a heap.
void IR_ALWAYS_INLINE HeapifyFromTop() { HeapifySubtreeDown(0, Size()); }
protected:
// Move up elements starting at index i to maintain the heap property.
// The operation stops when the element at index low_bound has been reached.
// It is assumed that the elements in [low_bound, i-1] are already arranged
// as a heap. This is an O(log n) algorithm.
void IR_ALWAYS_INLINE HeapifySubtreeUp(int low_bound, int i) {
while (i > low_bound) {
// parent = (i-2)/2 when i is even, or
// = (i-1)/2 = (i)/2 when i is odd.
int parent = (i & 1) ? (i >> 1) : ((i - 2) >> 1);
if (comparator_.Less(elements_[i], elements_[parent])) {
break;
} else {
Swap(elements_[parent], elements_[i]);
}
// Continue to heapify the affected sub-tree at root 'parent'.
i = parent;
}
}
// Heapify a sub-tree rooted at index i, all the way to the element at index
// high_bound-1. It is assumed that the elements in [i, high_bound-1] are already
// arranged as a heap. This is an O(log n) algorithm.
void IR_ALWAYS_INLINE HeapifySubtreeDown(int i, int high_bound) {
while (i < high_bound) {
int largest = i; // Initialize largest as the root
int left = (i << 1) + 1; // left = 2*i + 1
int right = left + 1; // right = 2*i + 2
// If the left child is larger than the root
if (left < high_bound && comparator_.Less(elements_[largest], elements_[left])) {
largest = left;
}
// If the right child is larger than the largest
if (right < high_bound && comparator_.Less(elements_[largest], elements_[right])) {
largest = right;
}
// If the largest is not the root
if (largest != i) {
Swap(elements_[i], elements_[largest]);
// Continue to heapify the affected sub-tree at root largest.
i = largest;
} else {
break;
}
}
}
// Swap two elements of type T.
void IR_ALWAYS_INLINE Swap(T& x, T& y) {
T t = x;
x = y;
y = t;
}
private:
// The array holding the elements.
std::vector<T> elements_;
// The comparator
const C& comparator_;
friend class PriorityQueueIterator<T, C>;
};
} // namespace impala