Median in a stream of integers (running integers) - GeeksforGeeks

12 May, 2022
Given that integers are read from a data stream. Find median of elements read so for in an efficient way. For simplicity assume, there are no duplicates. For example, let us consider the stream 5, 15, 1, 3 ...  
After reading 1st element of stream - 5 -> median - 5
After reading 2nd element of stream - 5, 15 -> median - 10
After reading 3rd element of stream - 5, 15, 1 -> median - 5
After reading 4th element of stream - 5, 15, 1, 3 -> median - 4, so on...
Making it clear, when the input size is odd, we take the middle element of sorted data. If the input size is even, we pick the average of the middle two elements in the sorted stream.Note that output is the effective median of integers read from the stream so far. Such an algorithm is called an online algorithm. Any algorithm that can guarantee the output of i-elements after processing i-th element, is said to be online algorithm. Let us discuss three solutions to the above problem. 
Method 1: Insertion SortIf we can sort the data as it appears, we can easily locate the median element. Insertion Sort is one such online algorithm that sorts the data appeared so far. At any instance of sorting, say after sorting i-th element, the first i elements of the array are sorted. The insertion sort doesn’t depend on future data to sort data input till that point. In other words, insertion sort considers data sorted so far while inserting the next element. This is the key part of insertion sort that makes it an online algorithm.However, insertion sort takes O(n2) time to sort n elements. Perhaps we can use binary search on insertion sort to find the location of the next element in O(log n) time. Yet, we can’t do data movement in O(log n) time. No matter how efficient the implementation is, it takes polynomial time in case of insertion sort.Interested readers can try the implementation of Method 1.
C++
Java
Python3
Javascript
// This code is contributed by Anjali Saxena #include <bits/stdc++.h> using namespace std; // Function to find position to insert current element of// stream using binary searchint binarySearch(int arr[], int item, int low, int high){    if (low >= high) {        return (item > arr[low]) ? (low + 1) : low;    }     int mid = (low + high) / 2;     if (item == arr[mid])        return mid + 1;     if (item > arr[mid])        return binarySearch(arr, item, mid + 1, high);     return binarySearch(arr, item, low, mid - 1);} // Function to print median of stream of integersvoid printMedian(int arr[], int n){    int i, j, pos, num;    int count = 1;     cout << "Median after reading 1"         << " element is " << arr[0] << "\n";     for (i = 1; i < n; i++) {        float median;        j = i - 1;        num = arr[i];         // find position to insert current element in sorted        // part of array        pos = binarySearch(arr, num, 0, j);         // move elements to right to create space to insert        // the current element        while (j >= pos) {            arr[j + 1] = arr[j];            j--;        }         arr[j + 1] = num;         // increment count of sorted elements in array        count++;         // if odd number of integers are read from stream        // then middle element in sorted order is median        // else average of middle elements is median        if (count % 2 != 0) {            median = arr[count / 2];        }        else {            median = (arr[(count / 2) - 1] + arr[count / 2])                     / 2;        }         cout << "Median after reading " << i + 1             << " elements is " << median << "\n";    }} // Driver Codeint main(){    int arr[] = { 5, 15, 1, 3, 2, 8, 7, 9, 10, 6, 11, 4 };    int n = sizeof(arr) / sizeof(arr[0]);     printMedian(arr, n);     return 0;}
// Java code to implement the approachimport java.util.*; class GFG {   // Function to find position to insert current element of  // stream using binary search  static int binarySearch(int arr[], int item, int low, int high)  {    if (low >= high) {      return (item > arr[low]) ? (low + 1) : low;    }     int mid = (low + high) / 2;     if (item == arr[mid])      return mid + 1;     if (item > arr[mid])      return binarySearch(arr, item, mid + 1, high);     return binarySearch(arr, item, low, mid - 1);  }   // Function to print median of stream of integers  static void printMedian(int arr[], int n)  {    int i, j, pos, num;    int count = 1;     System.out.println( "Median after reading 1"                       + " element is " + arr[0]);     for (i = 1; i < n; i++) {      float median;      j = i - 1;      num = arr[i];       // find position to insert current element in sorted      // part of array      pos = binarySearch(arr, num, 0, j);       // move elements to right to create space to insert      // the current element      while (j >= pos) {        arr[j + 1] = arr[j];        j--;      }       arr[j + 1] = num;       // increment count of sorted elements in array      count++;       // if odd number of integers are read from stream      // then middle element in sorted order is median      // else average of middle elements is median      if (count % 2 != 0) {        median = arr[count / 2];      }      else {        median = (arr[(count / 2) - 1] + arr[count / 2])          / 2;      }       System.out.println( "Median after reading " + (i + 1)                         + " elements is " + (int)median );    }  }    // Driver code  public static void main (String[] args) {     int arr[] = { 5, 15, 1, 3, 2, 8, 7, 9, 10, 6, 11, 4 };    int n = arr.length;     printMedian(arr, n);  }} // This code is contributed by sanjoy_62.
# Function to find position to insert current element of# stream using binary searchdef binarySearch(arr, item, low, high):     if (low >= high):        return (low + 1) if (item > arr[low]) else low     mid = (low + high) // 2     if (item == arr[mid]):        return mid + 1     if (item > arr[mid]):        return binarySearch(arr, item, mid + 1, high)     return binarySearch(arr, item, low, mid - 1) # Function to print median of stream of integersdef printMedian(arr, n):     i, j, pos, num = 0, 0, 0, 0    count = 1     print(f"Median after reading 1 element is {arr[0]}")     for i in range(1, n):        median = 0        j = i - 1        num = arr[i]         # find position to insert current element in sorted        # part of array        pos = binarySearch(arr, num, 0, j)         # move elements to right to create space to insert        # the current element        while (j >= pos):            arr[j + 1] = arr[j]            j -= 1         arr[j + 1] = num         # increment count of sorted elements in array        count += 1         # if odd number of integers are read from stream        # then middle element in sorted order is median        # else average of middle elements is median        if (count % 2 != 0):            median = arr[count // 2]         else:            median = (arr[(count // 2) - 1] + arr[count // 2]) // 2         print(f"Median after reading {i + 1} elements is {median} ") # Driver Codeif __name__ == "__main__":     arr = [5, 15, 1, 3, 2, 8, 7, 9, 10, 6, 11, 4]    n = len(arr)     printMedian(arr, n) # This code is contributed by rakeshsahni
<script>    // JavaScript implementation for the above approach     // Function to find position to insert current element of    // stream using binary search    const binarySearch = (arr, item, low, high) => {        if (low >= high) {            return (item > arr[low]) ? (low + 1) : low;        }         let mid = parseInt((low + high) / 2);         if (item == arr[mid])            return mid + 1;         if (item > arr[mid])            return binarySearch(arr, item, mid + 1, high);         return binarySearch(arr, item, low, mid - 1);    }     // Function to print median of stream of integers    const printMedian = (arr, n) => {        let i, j, pos, num;        let count = 1;         document.write(`Median after reading 1 element is ${arr[0]}<br/>`);         for (i = 1; i < n; i++) {            let median;            j = i - 1;            num = arr[i];             // find position to insert current element in sorted            // part of array            pos = binarySearch(arr, num, 0, j);             // move elements to right to create space to insert            // the current element            while (j >= pos) {                arr[j + 1] = arr[j];                j--;            }             arr[j + 1] = num;             // increment count of sorted elements in array            count++;             // if odd number of integers are read from stream            // then middle element in sorted order is median            // else average of middle elements is median            if (count % 2 != 0) {                median = arr[parseInt(count / 2)];            }            else {                median = parseInt((arr[parseInt(count / 2) - 1] + arr[parseInt(count / 2)]) / 2);            }             document.write(`Median after reading ${i + 1} elements is ${median}<br/>`);        }    }     // Driver Code    let arr = [5, 15, 1, 3, 2, 8, 7, 9, 10, 6, 11, 4];    let n = arr.length;     printMedian(arr, n);  // This code is contributed by rakeshsahni </script>
Median after reading 1 element is 5
Median after reading 2 elements is 10
Median after reading 3 elements is 5
Median after reading 4 elements is 4
Median after reading 5 elements is 3
Median after reading 6 elements is 4
Median after reading 7 elements is 5
Median after reading 8 elements is 6
Median after reading 9 elements is 7
Median after reading 10 elements is 6
Median after reading 11 elements is 7
Median after reading 12 elements is 6
Time Complexity: O(n2)
Space Complexity: O(1) 
Method 2: Augmented self-balanced binary search tree (AVL, RB, etc...)At every node of BST, maintain a number of elements in the subtree rooted at that node. We can use a node as the root of a simple binary tree, whose left child is self-balancing BST with elements less than root and right child is self-balancing BST with elements greater than root. The root element always holds effective median.If the left and right subtrees contain a same number of elements, the root node holds the average of left and right subtree root data. Otherwise, the root contains the same data as the root of subtree which is having more elements. After processing an incoming element, the left and right subtrees (BST) are differed utmost by 1.Self-balancing BST is costly in managing the balancing factor of BST. However, they provide sorted data which we don’t need. We need median only. The next method makes use of Heaps to trace the median.Method 3: HeapsSimilar to balancing BST in Method 2 above, we can use a max heap on the left side to represent elements that are less than effective median, and a min-heap on the right side to represent elements that are greater than effective median.After processing an incoming element, the number of elements in heaps differs utmost by 1 element. When both heaps contain the same number of elements, we pick the average of heaps root data as effective median. When the heaps are not balanced, we select effective median from the root of the heap containing more elements.Given below is the implementation of the above method. For the algorithm to build these heaps, please read the highlighted code. 
C
Python3
#include <iostream>using namespace std; // Heap capacity#define MAX_HEAP_SIZE (128)#define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0]) //// Utility functions // exchange a and binlinevoid Exch(int &a, int &b){    int aux = a;    a = b;    b = aux;} // Greater and Smaller are used as comparatorsbool Greater(int a, int b){    return a > b;} bool Smaller(int a, int b){    return a < b;} int Average(int a, int b){    return (a + b) / 2;} // Signum function// = 0  if a == b  - heaps are balanced// = -1 if a < b   - left contains less elements than right// = 1  if a > b   - left contains more elements than rightint Signum(int a, int b){    if( a == b )        return 0;     return a < b ? -1 : 1;} // Heap implementation// The functionality is embedded into// Heap abstract class to avoid code duplicationclass Heap{public:    // Initializes heap array and comparator required    // in heapification    Heap(int *b, bool (*c)(int, int)) : A(b), comp(c)    {        heapSize = -1;    }     // Frees up dynamic memory    virtual ~Heap()    {        if( A )        {            delete[] A;        }    }     // We need only these four interfaces of Heap ADT    virtual bool Insert(int e) = 0;    virtual int  GetTop() = 0;    virtual int  ExtractTop() = 0;    virtual int  GetCount() = 0; protected:     // We are also using location 0 of array    int left(int i)    {        return 2 * i + 1;    }     int right(int i)    {        return 2 * (i + 1);    }     int parent(int i)    {        if( i <= 0 )        {            return -1;        }         return (i - 1)/2;    }     // Heap array    int   *A;    // Comparator    bool  (*comp)(int, int);    // Heap size    int   heapSize;     // Returns top element of heap data structure    int top(void)    {        int max = -1;         if( heapSize >= 0 )        {            max = A[0];        }         return max;    }     // Returns number of elements in heap    int count()    {        return heapSize + 1;    }     // Heapification    // Note that, for the current median tracing problem    // we need to heapify only towards root, always    void heapify(int i)    {        int p = parent(i);         // comp - differentiate MaxHeap and MinHeap        // percolates up        if( p >= 0 && comp(A[i], A[p]) )        {            Exch(A[i], A[p]);            heapify(p);        }    }     // Deletes root of heap    int deleteTop()    {        int del = -1;         if( heapSize > -1)        {            del = A[0];             Exch(A[0], A[heapSize]);            heapSize--;            heapify(parent(heapSize+1));        }         return del;    }     // Helper to insert key into Heap    bool insertHelper(int key)    {        bool ret = false;         if( heapSize < MAX_HEAP_SIZE )        {            ret = true;            heapSize++;            A[heapSize] = key;            heapify(heapSize);        }         return ret;    }}; // Specialization of Heap to define MaxHeapclass MaxHeap : public Heap{private: public:    MaxHeap() : Heap(new int[MAX_HEAP_SIZE], &Greater)  {  }     ~MaxHeap()  { }     // Wrapper to return root of Max Heap    int GetTop()    {        return top();    }     // Wrapper to delete and return root of Max Heap    int ExtractTop()    {        return deleteTop();    }     // Wrapper to return # elements of Max Heap    int  GetCount()    {        return count();    }     // Wrapper to insert into Max Heap    bool Insert(int key)    {        return insertHelper(key);    }}; // Specialization of Heap to define MinHeapclass MinHeap : public Heap{private: public:     MinHeap() : Heap(new int[MAX_HEAP_SIZE], &Smaller) { }     ~MinHeap() { }     // Wrapper to return root of Min Heap    int GetTop()    {        return top();    }     // Wrapper to delete and return root of Min Heap    int ExtractTop()    {        return deleteTop();    }     // Wrapper to return # elements of Min Heap    int  GetCount()    {        return count();    }     // Wrapper to insert into Min Heap    bool Insert(int key)    {        return insertHelper(key);    }}; // Function implementing algorithm to find median so far.int getMedian(int e, int &m, Heap &l, Heap &r){    // Are heaps balanced? If yes, sig will be 0    int sig = Signum(l.GetCount(), r.GetCount());    switch(sig)    {    case 1: // There are more elements in left (max) heap         if( e < m ) // current element fits in left (max) heap        {            // Remove top element from left heap and            // insert into right heap            r.Insert(l.ExtractTop());             // current element fits in left (max) heap            l.Insert(e);        }        else        {            // current element fits in right (min) heap            r.Insert(e);        }         // Both heaps are balanced        m = Average(l.GetTop(), r.GetTop());         break;     case 0: // The left and right heaps contain same number of elements         if( e < m ) // current element fits in left (max) heap        {            l.Insert(e);            m = l.GetTop();        }        else        {            // current element fits in right (min) heap            r.Insert(e);            m = r.GetTop();        }         break;     case -1: // There are more elements in right (min) heap         if( e < m ) // current element fits in left (max) heap        {            l.Insert(e);        }        else        {            // Remove top element from right heap and            // insert into left heap            l.Insert(r.ExtractTop());             // current element fits in right (min) heap            r.Insert(e);        }         // Both heaps are balanced        m = Average(l.GetTop(), r.GetTop());         break;    }     // No need to return, m already updated    return m;} void printMedian(int A[], int size){    int m = 0; // effective median    Heap *left  = new MaxHeap();    Heap *right = new MinHeap();     for(int i = 0; i < size; i++)    {        m = getMedian(A[i], m, *left, *right);         cout << m << endl;    }     // C++ more flexible, ensure no leaks    delete left;    delete right;}// Driver codeint main(){    int A[] = {5, 15, 1, 3, 2, 8, 7, 9, 10, 6, 11, 4};    int size = ARRAY_SIZE(A);     // In lieu of A, we can also use data read from a stream    printMedian(A, size);     return 0;}
# codefrom heapq import heappush, heappop, heapifyimport mathminHeap=[]heapify(minHeap)maxHeap=[]heapify(maxHeap)def insertHeaps(num):    heappush(maxHeap,-num)                ### Pushing negative element to obtain a minHeap for    heappush(minHeap,-heappop(maxHeap))    ### the negative counterpart       if len(minHeap) > len(maxHeap):        heappush(maxHeap,-heappop(minHeap))     def getMedian():    if len(minHeap)!= len(maxHeap):        return -maxHeap[0]    else:        return (minHeap[0]- maxHeap[0])/2    if __name__== '__main__':    A= [5, 15, 1, 3, 2, 8, 7, 9, 10, 6, 11, 4]    n= len(A)    for i in range(n):        insertHeaps(A[i])        print(math.floor(getMedian()))
Time Complexity: If we omit the way how stream was read, complexity of median finding is O(N log N), as we need to read the stream, and due to heap insertions/deletions.
Auxiliary Space: O(N)At first glance the above code may look complex. If you read the code carefully, it is simple algorithm. Median of Stream of Running Integers using STL— Venki. Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above. 
amankd777
subhammahato348
abhishek0719kadiyan
saxenaanjali239
simmytarika5
rakeshsahni
arorakashish0911
Benequith
sanjoy_62
Adobe
Amazon
Apple
array-stream
Belzabar
Facebook
FactSet
Google
Hike
MAQ Software
median-finding
Microsoft
Morgan Stanley
Ola Cabs
Oracle
SAP Labs
statistical-algorithms
Yahoo
Heap
Mathematical
Morgan Stanley
Amazon
Microsoft
FactSet
Hike
Ola Cabs
Oracle
MAQ Software
Adobe
Google
SAP Labs
Facebook
Yahoo
Belzabar
Apple
Mathematical
Heap
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
HeapSort
Binary Heap
Huffman Coding | Greedy Algo-3
K'th Smallest/Largest Element in Unsorted Array | Set 1
k largest(or smallest) elements in an array
Program for Fibonacci numbers
C++ Data Types
Write a program to print all permutations of a given string
Set in C++ Standard Template Library (STL)
Coin Change | DP-7