Skip to content

Instantly share code, notes, and snippets.

@goldsborough
Created November 25, 2015 19:55
Show Gist options
  • Save goldsborough/0e274d2eb9da319a3329 to your computer and use it in GitHub Desktop.
Save goldsborough/0e274d2eb9da319a3329 to your computer and use it in GitHub Desktop.
A class to keep the median of a stream of values.
#ifndef MEDIAN_KEEPER_HPP
#define MEDIAN_KEEPER_HPP
#include <queue>
#include <vector>
template<typename T>
class MedianKeeper
{
public:
MedianKeeper() = default;
MedianKeeper(std::initializer_list<T> list)
{
for (const auto& item : list) push(item);
}
void push(const T& item)
{
if (seen() > 0 && item > _median)
{
_right.push(item);
if (_right.size() == _left.size() + 2)
{
_left.push(_right.top());
_right.pop();
}
}
else
{
_left.push(item);
if (_left.size() == _right.size() + 2)
{
_right.push(_left.top());
_left.pop();
}
}
_median = _compute();
}
T& operator()()
{
return median();
}
const T& operator()() const
{
return median();
}
T& median()
{
if (is_empty())
{
throw std::out_of_range("No values inserted yet!s");
}
return _median;
}
const T& median() const
{
if (is_empty())
{
throw std::out_of_range("No values inserted yet!s");
}
return _median;
}
T pop()
{
auto old = _median;
if (seen() % 2 == 0)
{
_left.pop();
_right.pop();
}
else if (_left.size() > _right.size()) _left.pop();
else _right.pop();
_median = _compute();
return old;
}
void clear()
{
_left.clear();
_right.clear();
}
size_t seen() const
{
return _left.size() + _right.size();
}
bool is_empty() const
{
return seen() == 0;
}
private:
T _compute()
{
if (seen() % 2 == 0)
{
return (_left.top() + _right.top()) / 2;
}
else if (_left.size() > _right.size()) return _left.top();
else return _right.top();
}
std::priority_queue<T> _left;
std::priority_queue<T, std::vector<int>, std::greater<T>> _right;
T _median;
};
#endif /* MEDIAN_KEEPER_HPP */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment