An easy way to do this is to maintain two sorted containers, one lower than the median, one greater.
When you find a new element, see what container to insert it into (so that all elements of lower are always less than or equal to all elements of upper), then rebalance counts so that the median is "in the gap" between them.
Yours sort of does this, but defines the lower range to be [.begin(), it)
and the upper to be [it, .end())
. I'd make them separate containers to reduce the amount of state you need to keep in your head to understand the code, unless performance was particularly important.
Maintain two sorted containers, low
and high
, with the following invariants:
low.size()
is the same as high.size()
or 1 larger
- Every element of
low
is less than or equal to every element of high
.
Then the median of low
union high
is low.last()
.
Assuming you have such a pair of containers, if you wanted to add an element x
, I would first maintain invariant 2 -- if low.empty()
or x <= low.last()
, stick it in low
, otherwise stick it in high
.
Next, maintain invariant 1: if high
has more elements than low, remove the lowest element from high
and add it to low
. If low
has 2 more elements than high
, remove the highest element from low
and add it to high
.
If we started with a low
and high
that obeyed the above two invariants, then after the above steps we still have a low
and high
that obeys these two invariants. And when the above two invariants hold, the median is low.last()
!