295. Find Median from Data Stream
Find Median from Data Stream
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.
- For example, for
arr = [2,3,4]
, the median is3
. - For example, for
arr = [2,3]
, the median is(2 + 3) / 2 = 2.5
.
Implement the MedianFinder class:
MedianFinder()
initializes theMedianFinder
object.void addNum(int num)
adds the integernum
from the data stream to the data structure.double findMedian()
returns the median of all elements so far. Answers within10-5
of the actual answer will be accepted.
Example 1:
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
Constraints:
-105 <= num <= 105
- There will be at least one element in the data structure before calling
findMedian
. - At most
5 * 104
calls will be made toaddNum
andfindMedian
.
Follow up:
- If all integer numbers from the stream are in the range
[0, 100]
, how would you optimize your solution? - If
99%
of all integer numbers from the stream are in the range[0, 100]
, how would you optimize your solution?
var MedianFinder = function() {
this.a = [];
};
/**
* @param {number} num
* @return {void}
*/
MedianFinder.prototype.addNum = function(num) {
this.a.push(num);
};
/**
* @return {number}
*/
MedianFinder.prototype.findMedian = function() {
this.a.sort((x, y) => x - y);
let n = this.a.length;
let m = n >> 1;
return n & 1 ? this.a[m] : (this.a[m - 1] + this.a[m]) / 2;
};
/**
* Your MedianFinder object will be instantiated and called as such:
* var obj = new MedianFinder()
* obj.addNum(num)
* var param_2 = obj.findMedian()
*/
var MedianFinder = function() {
this.ary = [];
};
/**
* @param {number} num
* @return {void}
*/
MedianFinder.prototype.addNum = function(num) {
var low = 0 ;
var high = this.ary.length-1;
while(low <= high)
{
var mid = Math.floor((high + low)/2);
if(this.ary[mid] < num)
{
low = mid+1;
}
else
{
high =mid-1;
}
}
// insert at location trick for javascript array.
this.ary.splice(low, 0, num);
};
/**
* @return {number}
*/
MedianFinder.prototype.findMedian = function() {
if(this.ary.length % 2 ==0)
{
var mid = this.ary.length/2;
return (this.ary[mid] + this.ary[mid-1])/2;
}
else
{
var mid = Math.floor(this.ary.length/2);
return (this.ary[mid]);
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* var obj = new MedianFinder()
* obj.addNum(num)
* var param_2 = obj.findMedian()
*/
/**
* initialize your data structure here.
*/
var MedianFinder = function() {
this.mxh=new heap((a,b)=>b-a,[]);
this.mnh=new heap((a,b)=>a-b,[]);
};
/**
* @param {number} num
* @return {void}
*/
MedianFinder.prototype.addNum = function(num) {
if(this.mxh.size()<=0)
return this.mxh.insert(num);
if(this.mnh.size()<=0){
if(num>=this.mxh.top())
return this.mnh.insert(num);
else{
this.mxh.insert(num);
this.mnh.insert(this.mxh.extract());
return;
}
}
if(num>=this.mnh.top()){
if(this.mnh.size()<=this.mxh.size())
return this.mnh.insert(num);
else{
this.mnh.insert(num);
this.mxh.insert(this.mnh.extract());
return;
}
}
else{
if(this.mxh.size()<=this.mnh.size())
return this.mxh.insert(num);
else{
this.mxh.insert(num);
this.mnh.insert(this.mxh.extract());
return;
}
}
};
/**
* @return {number}
*/
MedianFinder.prototype.findMedian = function() {
if(this.mnh.size()<=0)
return this.mxh.top();
if(this.mnh.size()==this.mxh.size())
return (this.mnh.top()+this.mxh.top())/2;
return this.mnh.size()>this.mxh.size()?this.mnh.top():this.mxh.top();
};
/**
* Your MedianFinder object will be instantiated and called as such:
* var obj = new MedianFinder()
* obj.addNum(num)
* var param_2 = obj.findMedian()
*/
/*Heap Implementation JavaScript*/
let heap = function(comparator,array){
this.ar=[...array];/*No mutation*/
this.comparator=comparator;//0/+ve/-ve
/*build heap*/
for(let i=parseInt((this.ar.length-1)/2);i>=0;i--){
this.heapify(i);
}
};
heap.prototype.heapify = function(i){
if(i>=this.ar.length)
return;
let temp=i;
if((2*i)+1<this.ar.length&&this.comparator(this.ar[(2*i)+1],this.ar[temp])<0)
temp=(2*i)+1;
if((2*i)+2<this.ar.length&&this.comparator(this.ar[(2*i)+2],this.ar[temp])<0)
temp=(2*i)+2;
if(temp!==i){
/*Swap*/
let copy=this.ar[temp];
this.ar[temp]=this.ar[i];
this.ar[i]=copy;
this.heapify(temp);
}
};
heap.prototype.insert = function(data){
if(data==null)
return false;
this.ar.push(data);
let i=this.ar.length-1;
let parent=parseInt((i-1)/2);
// comparator arguments should be inverted ???. Min Heap parent > child node swap heapify
// while(parent>=0&&this.comparator(this.ar[parent],this.ar[i])<0){//Agar ye hai fir to loop mein nhi jayega
while(parent>=0&&parent!=i&&this.comparator(this.ar[i],this.ar[parent])<0){
this.heapify(parent);
i=parent;
parent=parseInt((parent-1)/2);
}
return true;
};
heap.prototype.extract = function(){
let copy=this.ar[0];
this.ar[0]=this.ar[this.ar.length-1];
this.ar.pop();
this.heapify(0);
return copy;
};
heap.prototype.size = function(){
return this.ar.length;
};
heap.prototype.top = function(){
return this.ar[0];
};
As this list grows, it may become expensive to step through it and find the location of insertion. So, given that we'll be maintaining an ordered list, we can take advantage of bisection search to achieve faster search speed
addNum
is our workhorse:- Create a recursive function
searchForInsertionIndex()
- First account for special cases like empty or small lists
- Find the middle index of the current list (list.length / 2, rounded down)
- Check to see if num is equal to the middle or fits on either side (main base cases)
- If not, recurse on the correct half of the list and move our
storageIndex
pointer +/- slice length / 2 (with appropriate rounding) - After each number is added, we calculate the median value depending on whether the list is even/odd in length
- The Median is then stored on the object
- Create a recursive function
findMedian()
is just a getter forthis.median
var MedianFinder = function() {
this.storage = [];
this.median;
};
MedianFinder.prototype.addNum = function(num) {
const searchForInsertionIndex = (orderedList, storageIndex=-1) => {
const middleIndex = Math.floor(orderedList.length / 2);
if (storageIndex < 0) {
storageIndex = middleIndex;
}
const [leftNum, middleNum, rightNum] = [orderedList[middleIndex - 1], orderedList[middleIndex], orderedList[middleIndex + 1]];
if (orderedList.length === 0 || middleNum === num) {
return storageIndex;
}
if (orderedList.length === 1) {
return num > middleNum ? storageIndex + 1 : storageIndex;
}
if (orderedList.length === 2) {
if (num > middleNum) {
return storageIndex + 1;
}
if (num > leftNum) {
return storageIndex;
}
return storageIndex - 1;
}
if (rightNum >= num && middleNum < num) {
return storageIndex + 1;
}
if (middleNum > num && leftNum <= num) {
return storageIndex;
}
if (num > rightNum) {
const rightList = orderedList.slice(middleIndex + 1);
return searchForInsertionIndex(rightList, storageIndex + Math.floor(rightList.length / 2) + 1);
}
const leftList = orderedList.slice(0, middleIndex);
return searchForInsertionIndex(leftList, storageIndex - Math.ceil(leftList.length / 2));
};
const insertionIndex = searchForInsertionIndex(this.storage);
this.storage.splice(insertionIndex, 0, num);
const middleIndex = Math.floor(this.storage.length / 2);
this.median = this.storage.length % 2 === 0
? (this.storage[middleIndex] + this.storage[middleIndex - 1]) / 2
: Math.floor(this.storage[middleIndex]);
};
MedianFinder.prototype.findMedian = function() {
return this.median;
};
/**
* initialize your data structure here.
*/
class Node {
constructor(data){
this.data = data;
this.next = null;
}
}
var MedianFinder = function() {
this.head = null;
this.length = 0;
};
/**
* @param {number} num
* @return {void}
*/
MedianFinder.prototype.addNum = function(num) {
var newNode = new Node(num);
if(this.head == null){
this.head = newNode;
this.length++;
return;
}
// check if head.val is greater than num: Enter head
// elseif traverse and find when head.next.val is greater than val
// then insert:newNode.next = head.next, head.next = newNode;
// traverse ends, then last head.next = newnode.
if(this.head.data > num){
// insert at head
newNode.next = this.head;
this.head = newNode;
this.length++;
return;
}else{
var head = this.head;
while(head.next != null){
if(head.next.data > num){
break;
}
head = head.next;
}
newNode.next = head.next;
head.next = newNode;
this.length++;
return
}
};
/**
* @return {number}
*/
MedianFinder.prototype.findMedian = function() {
console.log(this.head)
var count = 0;
// when even this.length%2 == 0
// this.length/2 then return value of this index and next
// when odd this.length%2 != 0 math.round(length/2) -1<= roundup
if(this.length%2 != 0){
var returnIndex = Math.round(this.length/2) - 1;
head = this.head;
while(head){
if(count == returnIndex){
console.log(head.data)
return head.data;
}
head = head.next;
count++
}
}else{
var firstReturnIndex = ((this.length/2)-1);
head = this.head;
while(head){
if(count == firstReturnIndex){
console.log((head.data + head.next.datal)/2)
return (head.data + head.next.data)/2
}
head = head.next;
count++
}
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* var obj = new MedianFinder()
* obj.addNum(num)
* var param_2 = obj.findMedian()
*/
Conclusion
That’s all folks! In this post, we solved LeetCode problem 295. Find Median from Data Stream
I hope you have enjoyed this post. Feel free to share your thoughts on this.
You can find the complete source code on my GitHub repository. If you like what you learn. feel free to fork 🔪 and star ⭐ it.
In this blog, I have tried to collect & present the most important points to consider when improving Data structure and logic, feel free to add, edit, comment, or ask. For more information please reach me here
Happy coding!
Comments
Post a Comment