Heaps: Find the Running Median

Sort by

recency

|

279 Discussions

|

  • + 0 comments

    Using a multiset and moving iterator to median left/right.

    `
    void runningMedian(vector<int> arr) {
        // Print your answer within the function
        std::cout << std::fixed;
        std::cout << std::setprecision(1);
        
        std::multiset<int> ms;
        float median = 0.0;
        
        cout << float(arr[0]) << endl;
        ms.insert(arr[0]);
        std::multiset<int>::iterator med = ms.begin();
        
        for(size_t cs = 1; cs < arr.size(); cs++)
        {
           ms.insert(arr[cs]);
            // odd,
            if(cs % 2 == 0){
                if(arr[cs] >= *med) med = next(med, 1);
                median = *med;
            }
            //even
           else{
               if(arr[cs] < *med) med = next(med, -1);
                median = (*med + *(std::next(med, 1)))/ 2.0;
           }
           cout << float(median) << endl; 
        }
    }
    
  • + 0 comments

    Ruby solution with custom Binary Heap implementation (as there is no built-in Heap in Ruby's core lib)

    class AbstractBinHeap
      def initialize
        @store = []
      end
    
      def empty?
        @store.empty?
      end
    
      def size
        @store.size
      end
      alias length size
    
      def to_s
        tmp = dup
        order = []
        order << tmp.pop until tmp.empty?
        order.join(' ')
      end
      alias inspect to_s
    
      def add(e)
        @store << e
        heapify_up(@store.size - 1)
      end
      alias << add
    
      def peek
        if empty?
          nil
        else
          @store[0]
        end
      end
      alias next peek
    
      def pop
        return nil if empty?
    
        e = @store[0]
        @store[0] = @store[-1]
        @store.pop
        heapify_down(0)
        e
      end
    
      # Delete first occurence of e.
      def delete(e)
        pos = @store.index(e)
        if pos.nil?
          nil
        else
          delete_at(pos)
        end
      end
    
      protected
    
      attr_writer :store
    
      def dup
        cl = self.class.new
        cl.store = @store.dup
        cl
      end
    
      private
    
      def delete_at(pos)
        return nil if pos >= @store.length
    
        e = @store[pos]
        @store[pos] = most_sig_val
        heapify_up(pos)
        pop
        e
      end
    
      def parent(pos)
        (pos - 1) / 2
      end
    
      def lchild(pos)
        pos * 2 + 1
      end
    
      def rchild(pos)
        pos * 2 + 2
      end
    
      def heapify_up(pos)
        return if pos == 0 || pos >= @store.length
    
        loop do
          parent = (pos - 1) / 2
          break unless pos > 0 && compare(@store[pos], @store[parent])
    
          @store[pos], @store[parent] = @store[parent], @store[pos]
          pos = parent
        end
      end
    
      def heapify_down(pos)
        return if pos >= @store.length
    
        while lchild(pos) < @store.length
          lchild = lchild(pos)
          rchild = rchild(pos)
          minchild = if rchild < @store.length
                       compare(@store[lchild], @store[rchild]) ? lchild : rchild
                     else
                       lchild
                     end
    
          if compare(@store[minchild], @store[pos])
            @store[pos], @store[minchild] = @store[minchild], @store[pos]
            pos = minchild
          else
            break
          end
        end
      end
    end
    
    class MinHeap < AbstractBinHeap
      private
    
      def compare(obj1, obj2)
        obj1 < obj2
      end
    
      def most_sig_val
        -1 * Float::INFINITY
      end
    end
    
    class MaxHeap < AbstractBinHeap
      private
    
      def compare(obj1, obj2)
        obj1 > obj2
      end
    
      def most_sig_val
        Float::INFINITY
      end
    end
    
    def running_median(arr)
      smaller = MaxHeap.new
      larger = MinHeap.new
      arr.map do |e|
        if smaller.empty? || e <= smaller.peek
          smaller << e
        else
          larger << e
        end
    
        if smaller.size < larger.size
          smaller << larger.pop
        elsif smaller.size - 1 > larger.size
          larger << smaller.pop
        end
    
        if larger.empty?
          smaller.peek
        else
          smaller.size == larger.size ? (smaller.peek + larger.peek) / 2 : smaller.peek
        end
      end
    end
    
    n = gets.to_i
    arr = Array.new(n)
    n.times { |i| arr[i] = gets.to_f }
    puts(running_median(arr).map { |m| format('%.1f', m) })
    
  • + 0 comments

    Solution with my original Heap class and an optimized way faster one:

    class Heap { // My original implementation - cagils
      constructor(comp) {
        this.heap = [];
        if (comp === "max" || !comp) this.comp = (a, b) => a > b;
        else if (comp === "min") this.comp = (a, b) => a < b;
        else this.comp = comp;
      }
      _heapify() {
        for (let i = parseInt(this.heap.length / 2 - 1); i >= 0; i--)
          Heap.heapify(this.heap, this.heap.length, i, this.comp);
      }
      static heapify(arr, n, i, comp) {
        let first = i;
        let li = 2*i+1;
        let ri = 2*i+2;
        if (li < n && comp(arr[li], arr[first])) first = li;
        if (ri < n && comp(arr[ri], arr[first])) first = ri;
        if (first !== i) {
          [arr[i], arr[first]] = [arr[first], arr[i]];
          Heap.heapify(arr, n, first, comp);
        }
      }
      push = (data) => {
        this.heap.push(data);
        this._heapify();
      };
      remove(data) {
        const size = this.heap.length;
        let i = this.heap.findIndex((e) => e === data);
        if (i === -1) i = size;
        [this.heap[i], this.heap[size - 1]] = [this.heap[size - 1], this.heap[i]];
        this.heap.splice(size - 1);
        this._heapify();
        return data;
      }
      peek = () => this.heap[0];
      pop = () => this.remove(this.heap[0]);
      size = () => this.heap.length;
      getList = () => this.heap;
    }
    
    class _Heap { // faster implementation
        constructor(comp) {
            this.a = [];
            if (comp === "min" || !comp) this.comp = (a, b) => a < b;
            else if (comp === "max") this.comp = (a, b) => a > b;
            else this.comp = comp;
        }
        
        up (i) {
            //const pi = (i - 1) >> 1;//Math.floor((i - 1) / 2)
            const pi = Math.floor((i - 1) / 2);
            if(this.comp(this.a[i], this.a[pi])) {
                [this.a[pi], this.a[i]] = [this.a[i], this.a[pi]];
                this.up(pi);
            }
        }
        
        down (i) {
            const lci = i * 2 + 1;
            const rci = lci + 1;
            const pi = i;
            const a_i = this.a[i];
            if(this.comp(this.a[lci], a_i) || this.comp(this.a[rci], a_i)) {
                if(this.comp(this.a[lci], a_i) && this.comp(this.a[rci], a_i))
                    i = this.comp(this.a[lci], this.a[rci]) ? lci : rci;
                else
                    i = this.comp(this.a[lci], a_i) ? lci : rci;
                this.a[pi] = this.a[i];
                this.a[i] = a_i;
                this.down(i);
            }
        }
        
        push(num) {
            this.a.push(num);
            this.up(this.a.length - 1);
        }
        
        pop() {
            const deletedVal = this.a[0];
            this.a.length > 1 ? this.a[0] = this.a.pop() : this.a.pop();
            this.down(0);
            return deletedVal;
        }
        
        peek() { return this.a[0] }
        size = () => this.a.length;
        getList = () => this.a;
    }
    
    function runningMedian(a) {
        const highers = new _Heap('min');
        const lowers = new _Heap('max');
        
        for (const n of a) {
            if (lowers.size() === 0 || lowers.peek() > n) lowers.push(n); else highers.push(n);        
            let [big, small] = highers.size() > lowers.size() ? [highers, lowers] : [lowers, highers];
            let diff = big.size() - small.size();
            if (big.size() - small.size() > 1) {
                small.push(big.pop());
                diff = 0;
            }
            const out = diff === 0 ? (big.peek() + small.peek()) / 2 : big.peek();
            console.log(out.toFixed(1))
        }
    }
    
  • + 0 comments

    C++ Heaps Approach

    void runningMedian(vector<int> arr)
    {
        priority_queue<int, vector<int>> mxheap;
        priority_queue<int, vector<int>, greater<int>> minheap;
        float med = arr[0];
        mxheap.push(arr[0]);
         cout << fixed << setprecision(1) << med << endl;
        for (int i = 1; i < arr.size(); i++)
        {
    
            if (mxheap.size() == minheap.size())
            {
                // if (mxheap.size() == 0)
                // {
                //     mxheap.push(arr[i]);
                //     med = mxheap.top();
                //     cout << med << endl;
                //     return;
                // }
                if (arr[i] < mxheap.top())
                {
                    mxheap.push(arr[i]);
                    med = (double)mxheap.top();
                }
                else
                {
                    minheap.push(arr[i]);
                    med = (double)minheap.top();
                }
            }
            else
            {
                if (mxheap.size() > minheap.size())
                {
                    if (arr[i] >= mxheap.top())
                    {
                        minheap.push(arr[i]);
                    }
                    else
                    {
                        int temp = mxheap.top();
                        mxheap.pop();
                        mxheap.push(arr[i]);
                        minheap.push(temp);
                    }
                    med = (mxheap.top() + minheap.top()) / 2.0;
                }
                else
                {
                    if (arr[i] <= minheap.top())
                    {
                        mxheap.push(arr[i]);
                    }
                    else
                    {
                        int temp = minheap.top();
                        minheap.pop();
                        minheap.push(arr[i]);
                        mxheap.push(temp);
                    }
                    med = (mxheap.top() + minheap.top()) / 2.0;
                }
            }
            printf("%.1f \n",med);
        }
    }
    
  • + 0 comments

    This works fine, but as I understand time complexity if O(n^2) due to add operation

    List<Integer> aList = new ArrayList(n);
            Collections.fill(aList, Integer.MAX_VALUE);
    
            int lastIndex = -1;
            for (int i = 0; i < n; i++) {
                int aItem = scanner.nextInt();
                scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
    
                int index = Collections.binarySearch(aList, aItem);
                if (index < 0) {
                    index = Math.abs(index) - 1;
                }
                aList.add(index, aItem);
    
                lastIndex++;
    
                if ((lastIndex + 1) % 2 == 0) {
                    int item1 = aList.get((lastIndex + 1) / 2);
                    int item2 = aList.get(((lastIndex + 1) / 2) - 1);
                    System.out.println((item1 + item2) / 2.0);
                } else {
                    System.out.println(aList.get((lastIndex + 1) / 2) / 1.0);
                }
            }