Given an array of integer arr[] and an integer k, the task is to find the median of each window of size k starting from the left and moving towards the right by one position each time. Examples:
Input: arr[] = {-1, 5, 13, 8, 2, 3, 3, 1}, k = 3 Output: 5 8 8 3 3 3 Input: arr[] = {-1, 5, 13, 8, 2, 3, 3, 1}, k = 4 Output: 6.5 6.5 5.5 3.0 2.5
Approach: Create a pair class to hold the items and their index. It also implements the comparable interface so that compareTo() method will be invoked by the Treeset to find the nodes. Note that the two pairs are equal only when their indices are equal. This is important since a window can contain duplicates and we may end up deleting multiple items in single remove() call if we only check for the value. The idea is to maintain two sorted sets (minSet and maxSet) of Pair objects of length (k / 2) and (k / 2) + 1 depending on whether k is even or odd, minSet will always contain the first set of numbers (smaller) of window k and maxSet will contain the second set of numbers (larger). As we move our window, we will remove elements from either of the sets (log n) and add a new element (log n) maintaining the minSet and maxSet rule specified above. Below is the implementation of the above approach:
#include <iostream> #include <set> #include <vector> using namespace std;
// Pair class for the value and its index struct Pair {
int value, index;
// Constructor
Pair( int v, int p)
: value(v)
, index(p)
{
}
// This method will be used by the set to
// search a value by index and setting the tree
// nodes (left or right)
bool operator<( const Pair& o) const
{
// Two nodes are equal only when
// their indices are the same
if (index == o.index) {
return false ;
}
else if (value == o.value) {
return index < o.index;
}
else {
return value < o.value;
}
}
// Function to return the value
// of the current object
int getValue() const { return value; }
// Update the value and the position
// for the same object to save space
void renew( int v, int p)
{
value = v;
index = p;
}
// Override the << operator for easy printing
friend ostream& operator<<(ostream& os,
const Pair& pair)
{
return os << "(" << pair.value << ", " << pair.index
<< ")" ;
}
}; // Function to print the median for the current window void printMedian(multiset<Pair>& minSet,
multiset<Pair>& maxSet, int window)
{ // If the window size is even then the
// median will be the average of the
// two middle elements
if (window % 2 == 0) {
auto minSetLast = minSet.rbegin();
auto maxSetFirst = maxSet.begin();
double median = (minSetLast->getValue()
+ maxSetFirst->getValue())
/ 2.0;
cout << median << " " ;
}
// Else it will be the middle element
else {
if (minSet.size() > maxSet.size()) {
auto minSetLast = minSet.rbegin();
cout << minSetLast->getValue() << " " ;
}
else {
auto maxSetFirst = maxSet.begin();
cout << maxSetFirst->getValue() << " " ;
}
}
} // Function to find the median // of every window of size k void findMedian( int arr[], int n, int k)
{ multiset<Pair> minSet;
multiset<Pair> maxSet;
// To hold the pairs, we will use a vector
vector<Pair> windowPairs(
k, Pair(0, 0)); // Initialize with dummy values
for ( int i = 0; i < k; i++) {
windowPairs[i].renew(arr[i], i);
}
// Add k/2 items to maxSet
for ( int i = 0; i < k / 2; i++) {
maxSet.insert(windowPairs[i]);
}
for ( int i = k / 2; i < k; i++) {
// Below logic is to maintain the
// maxSet and the minSet criteria
if (arr[i] < maxSet.begin()->getValue()) {
minSet.insert(windowPairs[i]);
}
else {
minSet.insert(*maxSet.begin());
maxSet.erase(maxSet.begin());
maxSet.insert(windowPairs[i]);
}
}
printMedian(minSet, maxSet, k);
for ( int i = k; i < n; i++) {
// Get the pair at the start of the window, this
// will reset to 0 at every k, 2k, 3k, ...
Pair& temp = windowPairs[i % k];
if (temp.getValue()
<= minSet.rbegin()->getValue()) {
// Remove the starting pair of the window
minSet.erase(minSet.find(temp));
// Renew window start to new window end
temp.renew(arr[i], i);
// Below logic is to maintain the
// maxSet and the minSet criteria
if (temp.getValue()
< maxSet.begin()->getValue()) {
minSet.insert(temp);
}
else {
minSet.insert(*maxSet.begin());
maxSet.erase(maxSet.begin());
maxSet.insert(temp);
}
}
else {
maxSet.erase(maxSet.find(temp));
temp.renew(arr[i], i);
// Below logic is to maintain the
// maxSet and the minSet criteria
if (temp.getValue()
> minSet.rbegin()->getValue()) {
maxSet.insert(temp);
}
else {
maxSet.insert(*minSet.rbegin());
minSet.erase(--minSet.end());
minSet.insert(temp);
}
}
printMedian(minSet, maxSet, k);
}
} // Driver code int main()
{ int arr[] = { 0, 9, 1, 8, 2, 7, 3, 6, 4, 5 };
int k = 3;
int n = sizeof (arr) / sizeof (arr[0]);
findMedian(arr, n, k);
return 0;
} |
// Java implementation of the approach import java.util.TreeSet;
public class GFG {
// Pair class for the value and its index
static class Pair implements Comparable<Pair> {
private int value, index;
// Constructor
public Pair( int v, int p)
{
value = v;
index = p;
}
// This method will be used by the treeset to
// search a value by index and setting the tree
// nodes (left or right)
@Override
public int compareTo(Pair o)
{
// Two nodes are equal only when
// their indices are same
if (index == o.index) {
return 0 ;
}
else if (value == o.value) {
return Integer.compare(index, o.index);
}
else {
return Integer.compare(value, o.value);
}
}
// Function to return the value
// of the current object
public int value()
{
return value;
}
// Update the value and the position
// for the same object to save space
public void renew( int v, int p)
{
value = v;
index = p;
}
@Override
public String toString()
{
return String.format( "(%d, %d)" , value, index);
}
}
// Function to print the median for the current window
static void printMedian(TreeSet<Pair> minSet,
TreeSet<Pair> maxSet, int window)
{
// If the window size is even then the
// median will be the average of the
// two middle elements
if (window % 2 == 0 ) {
System.out.print((minSet.last().value()
+ maxSet.first().value())
/ 2.0 );
System.out.print( " " );
}
// Else it will be the middle element
else {
System.out.print(minSet.size() > maxSet.size()
? minSet.last().value()
: maxSet.first().value());
System.out.print( " " );
}
}
// Function to find the median
// of every window of size k
static void findMedian( int arr[], int k)
{
TreeSet<Pair> minSet = new TreeSet<>();
TreeSet<Pair> maxSet = new TreeSet<>();
// To hold the pairs, we will keep renewing
// these instead of creating the new pairs
Pair[] windowPairs = new Pair[k];
for ( int i = 0 ; i < k; i++) {
windowPairs[i] = new Pair(arr[i], i);
}
// Add k/2 items to maxSet
for ( int i = 0 ; i < k / 2 ; i++) {
maxSet.add(windowPairs[i]);
}
for ( int i = k / 2 ; i < k; i++) {
// Below logic is to maintain the
// maxSet and the minSet criteria
if (arr[i] < maxSet.first().value()) {
minSet.add(windowPairs[i]);
}
else {
minSet.add(maxSet.pollFirst());
maxSet.add(windowPairs[i]);
}
}
printMedian(minSet, maxSet, k);
for ( int i = k; i < arr.length; i++) {
// Get the pair at the start of the window, this
// will reset to 0 at every k, 2k, 3k, ...
Pair temp = windowPairs[i % k];
if (temp.value() <= minSet.last().value()) {
// Remove the starting pair of the window
minSet.remove(temp);
// Renew window start to new window end
temp.renew(arr[i], i);
// Below logic is to maintain the
// maxSet and the minSet criteria
if (temp.value() < maxSet.first().value()) {
minSet.add(temp);
}
else {
minSet.add(maxSet.pollFirst());
maxSet.add(temp);
}
}
else {
maxSet.remove(temp);
temp.renew(arr[i], i);
// Below logic is to maintain the
// maxSet and the minSet criteria
if (temp.value() > minSet.last().value()) {
maxSet.add(temp);
}
else {
maxSet.add(minSet.pollLast());
minSet.add(temp);
}
}
printMedian(minSet, maxSet, k);
}
}
// Driver code
public static void main(String[] args)
{
int [] arr = new int [] { 0 , 9 , 1 , 8 , 2 ,
7 , 3 , 6 , 4 , 5 };
int k = 3 ;
findMedian(arr, k);
}
} |
// C# implementation of the approach using System;
using System.Collections.Generic;
class GFG {
// Pair class for the value and its index
class Pair : IComparable<Pair> {
private int value, index;
// Constructor
public Pair( int v, int p)
{
value = v;
index = p;
}
// This method will be used by the treeset to
// search a value by index and setting the tree
// nodes (left or right)
public int CompareTo(Pair o)
{
// Two nodes are equal only when
// their indices are same
if (index == o.index) {
return 0;
}
else if (value == o.value) {
return index.CompareTo(o.index);
}
else {
return value.CompareTo(o.value);
}
}
// Function to return the value
// of the current object
public int Value() { return value; }
// Update the value and the position
// for the same object to save space
public void Renew( int v, int p)
{
value = v;
index = p;
}
public override string ToString()
{
return $ "({value}, {index})" ;
}
}
// Function to print the median for the current window
static void PrintMedian(SortedSet<Pair> minSet,
SortedSet<Pair> maxSet,
int window)
{
// If the window size is even then the
// median will be the average of the
// two middle elements
if (window % 2 == 0) {
Console.Write(
(minSet.Max.Value() + maxSet.Min.Value())
/ 2.0
+ " " );
}
else {
// Else it will be the middle element
Console.Write((minSet.Count > maxSet.Count
? minSet.Max.Value()
: maxSet.Min.Value())
+ " " );
}
}
// Function to find the median
// of every window of size k
static void FindMedian( int [] arr, int k)
{
SortedSet<Pair> minSet = new SortedSet<Pair>();
SortedSet<Pair> maxSet = new SortedSet<Pair>();
// To hold the pairs, we will keep renewing
// these instead of creating the new pairs
Pair[] windowPairs = new Pair[k];
// Add k/2 items to maxSet
for ( int i = 0; i < k; i++) {
windowPairs[i] = new Pair(arr[i], i);
}
for ( int i = 0; i < k / 2; i++) {
maxSet.Add(windowPairs[i]);
}
for ( int i = k / 2; i < k; i++) {
// Below logic is to maintain the
// maxSet and the minSet criteria
if (arr[i] < maxSet.Min.Value()) {
minSet.Add(windowPairs[i]);
}
else {
minSet.Add(maxSet.Min);
maxSet.Remove(maxSet.Min);
maxSet.Add(windowPairs[i]);
}
}
PrintMedian(minSet, maxSet, k);
for ( int i = k; i < arr.Length; i++) {
// Get the pair at the start of the window, this
// will reset to 0 at every k, 2k, 3k, ...
Pair temp = windowPairs[i % k];
if (temp.Value() <= minSet.Max.Value()) {
minSet.Remove(temp);
temp.Renew(arr[i], i);
// Below logic is to maintain the
// maxSet and the minSet criteria
if (temp.Value() < maxSet.Min.Value()) {
minSet.Add(temp);
}
else {
minSet.Add(maxSet.Min);
maxSet.Remove(maxSet.Min);
maxSet.Add(temp);
}
}
else {
maxSet.Remove(temp);
temp.Renew(arr[i], i);
// Below logic is to maintain the
// maxSet and the minSet criteria
if (temp.Value() > minSet.Max.Value())
maxSet.Add(temp);
else {
maxSet.Add(minSet.Max);
minSet.Remove(minSet.Max);
minSet.Add(temp);
}
}
PrintMedian(minSet, maxSet, k);
}
}
// Driver code
public static void Main( string [] args)
{
int [] arr
= new int [] { 0, 9, 1, 8, 2, 7, 3, 6, 4, 5 };
int k = 3;
FindMedian(arr, k);
}
} // This code is contributed by phasing17. |
class GFG { // Pair class for the value and its index
class Pair {
constructor(v, p) {
this .value = v;
this .index = p;
}
compareTo(o) {
// Two nodes are equal only when their indices are the same
if ( this .index === o.index) {
return 0;
} else if ( this .value === o.value) {
return this .index - o.index;
} else {
return this .value - o.value;
}
}
value() {
return this .value;
}
renew(v, p) {
this .value = v;
this .index = p;
}
}
// Function to print the median for the current window
printMedian(minSet, maxSet, window) {
if (window % 2 === 0) {
console.log((minSet[minSet.length - 1].value() + maxSet[0].value()) / 2.0);
} else {
console.log(minSet.length > maxSet.length ? minSet[minSet.length - 1].value() : maxSet[0].value());
}
}
// Function to find the median of every window of size k
findMedian(arr, k) {
const minSet = [];
const maxSet = [];
const windowPairs = new Array(k);
for (let i = 0; i < k; i++) {
windowPairs[i] = new Pair(arr[i], i);
}
for (let i = 0; i < k / 2; i++) {
maxSet.push(windowPairs[i]);
}
for (let i = k / 2; i < k; i++) {
if (arr[i] < maxSet[0].value()) {
minSet.push(windowPairs[i]);
} else {
minSet.push(maxSet.shift());
maxSet.push(windowPairs[i]);
}
}
this .printMedian(minSet, maxSet, k);
for (let i = k; i < arr.length; i++) {
const temp = windowPairs[i % k];
if (temp.value() <= minSet[minSet.length - 1].value()) {
minSet.pop();
temp.renew(arr[i], i);
if (temp.value() < maxSet[0].value()) {
minSet.push(temp);
} else {
minSet.push(maxSet.shift());
maxSet.push(temp);
}
} else {
maxSet.shift();
temp.renew(arr[i], i);
if (temp.value() > minSet[minSet.length - 1].value()) {
maxSet.push(temp);
} else {
maxSet.push(minSet.pop());
minSet.push(temp);
}
}
this .printMedian(minSet, maxSet, k);
}
}
} // Driver code const gfg = new GFG();
const arr = [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]; const k = 3; gfg.findMedian(arr, k); |
1 8 2 7 3 6 4 5
Time Complexity: O(n log k), where n is the size of the input array and k is the size of the sliding window.
Space Complexity: O(k), size of sliding window