The median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value, and the median is the mean of the two middle values.
Implement the MedianFinder class:
Input:
[“MedianFinder”, “addNum”, “addNum”, “findMedian”, “addNum”, “findMedian”]
[[], [1], [2], [], [3], []]
Output:
[null, null, null, 1.5, null, 2.0]
Explanation:
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1); // arr = [1]
medianFinder.addNum(2); // arr = [1, 2]
medianFinder.findMedian(); // return 1.5 (i.e., (1 + 2) / 2)
medianFinder.addNum(3); // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0
From: LeetCode
Link: 295. Find Median from Data Stream
To efficiently support these operations, we use two heaps:
Here’s how the data structure works:
By maintaining the sizes of the heaps (such that the max heap is either equal in size to the min heap or has one more element), we can ensure that:
When a new element is added:
When we need to find the median:
typedef struct {
int* maxHeap;
int* minHeap;
int maxHeapSize;
int minHeapSize;
} MedianFinder;
MedianFinder* medianFinderCreate() {
MedianFinder* obj = (MedianFinder*) malloc(sizeof(MedianFinder));
obj->maxHeap = (int*) malloc(sizeof(int) * 50000); // Allocate memory for max heap
obj->minHeap = (int*) malloc(sizeof(int) * 50000); // Allocate memory for min heap
obj->maxHeapSize = 0;
obj->minHeapSize = 0;
return obj;
}
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
void maxHeapify(MedianFinder* obj, int i) {
int left = 2 * i + 1;
int right = 2 * i + 2;
int largest = i;
if (left < obj->maxHeapSize && obj->maxHeap[left] > obj->maxHeap[largest])
largest = left;
if (right < obj->maxHeapSize && obj->maxHeap[right] > obj->maxHeap[largest])
largest = right;
if (largest != i) {
swap(&obj->maxHeap[i], &obj->maxHeap[largest]);
maxHeapify(obj, largest);
}
}
void minHeapify(MedianFinder* obj, int i) {
int left = 2 * i + 1;
int right = 2 * i + 2;
int smallest = i;
if (left < obj->minHeapSize && obj->minHeap[left] < obj->minHeap[smallest])
smallest = left;
if (right < obj->minHeapSize && obj->minHeap[right] < obj->minHeap[smallest])
smallest = right;
if (smallest != i) {
swap(&obj->minHeap[i], &obj->minHeap[smallest]);
minHeapify(obj, smallest);
}
}
void addNumToMaxHeap(MedianFinder* obj, int num) {
obj->maxHeap[obj->maxHeapSize] = num;
int i = obj->maxHeapSize;
obj->maxHeapSize++;
while (i != 0 && obj->maxHeap[(i - 1) / 2] < obj->maxHeap[i]) {
swap(&obj->maxHeap[i], &obj->maxHeap[(i - 1) / 2]);
i = (i - 1) / 2;
}
}
void addNumToMinHeap(MedianFinder* obj, int num) {
obj->minHeap[obj->minHeapSize] = num;
int i = obj->minHeapSize;
obj->minHeapSize++;
while (i != 0 && obj->minHeap[(i - 1) / 2] > obj->minHeap[i]) {
swap(&obj->minHeap[i], &obj->minHeap[(i - 1) / 2]);
i = (i - 1) / 2;
}
}
int extractMax(MedianFinder* obj) {
int max = obj->maxHeap[0];
obj->maxHeap[0] = obj->maxHeap[obj->maxHeapSize - 1];
obj->maxHeapSize--;
maxHeapify(obj, 0);
return max;
}
int extractMin(MedianFinder* obj) {
int min = obj->minHeap[0];
obj->minHeap[0] = obj->minHeap[obj->minHeapSize - 1];
obj->minHeapSize--;
minHeapify(obj, 0);
return min;
}
void medianFinderAddNum(MedianFinder* obj, int num) {
if (obj->maxHeapSize == 0 || num < obj->maxHeap[0]) {
addNumToMaxHeap(obj, num);
} else {
addNumToMinHeap(obj, num);
}
if (obj->maxHeapSize > obj->minHeapSize + 1) {
addNumToMinHeap(obj, extractMax(obj));
} else if (obj->minHeapSize > obj->maxHeapSize) {
addNumToMaxHeap(obj, extractMin(obj));
}
}
double medianFinderFindMedian(MedianFinder* obj) {
if (obj->maxHeapSize > obj->minHeapSize) {
return obj->maxHeap[0];
} else if (obj->maxHeapSize < obj->minHeapSize) {
return obj->minHeap[0];
} else {
return ((double)obj->maxHeap[0] + (double)obj->minHeap[0]) / 2;
}
}
void medianFinderFree(MedianFinder* obj) {
free(obj->maxHeap);
free(obj->minHeap);
free(obj);
}