Skip to content

Commit 84ac99f

Browse files
Add 'Find Median from Data Stream'
1 parent 57e9703 commit 84ac99f

File tree

4 files changed

+116
-0
lines changed

4 files changed

+116
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ A collection of LeetCode solutions
1414

1515
[Contains Duplicate](./src/contains_duplicate.py)
1616

17+
[Find Median from Data Stream](./src/find_median_from_data_stream.py)
18+
1719
[Flood Fill](./src/flood_fill.py)
1820

1921
[House Robber](./src/house_robber.py)

TODO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
- [ ] Add recursive approach to 'Reverse Linked List'
1212
- [ ] Add recursive approach to 'Merge Two Sorted Lists'
1313
- [ ] Add a *divide and conquer* approach to 'Merge k Sorted Lists'. This approach has O(nlog n) time complexity, but O(1) space complexity.
14+
- [ ] Add self-balancing BST approach to 'Find Median from Data Stream'

src/find_median_from_data_stream.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""
2+
295. Find Median from Data Stream
3+
4+
https://leetcode.com/problems/find-median-from-data-stream
5+
6+
NOTES
7+
* There are a few different approaches to this problem:
8+
1. Maintain an unsorted array. Sort the array to find the median.
9+
2. Maintain a sorted array.
10+
3. Use two heaps.
11+
4. Self-balancing BST.
12+
13+
We will focus on 3 and 4, since they are most interesting solutions.
14+
15+
Two Heaps
16+
---------
17+
Two heaps are maintained:
18+
* A max-heap for elements smaller than the median
19+
* A min-heap for elements greater than the median
20+
21+
If the heaps are balanced, the median can be calculated from the root of each
22+
heap. If the heaps are unbalanced (the max-heap contains one more element than
23+
the min-heap), the median is the root of the max-heap.
24+
25+
When performing an insertion, the new element is compared to the root of the
26+
max-heap. If the element is less than or equal to the median, the element is
27+
added to the max-heap. If the element is greater than the median, it is added
28+
to the min-heap. If the insertion causes the heaps to become unbalanced, the
29+
root of the offending heap is removed and added to the other heap.
30+
31+
Self-balancing BST
32+
------------------
33+
TODO
34+
"""
35+
36+
import heapq
37+
38+
39+
class MedianFinder:
40+
def __init__(self) -> None:
41+
# max-heap for elements smaller than the median
42+
self.max_heap: list[int] = []
43+
# min-heap for elements greater than the median
44+
self.min_heap: list[int] = []
45+
46+
def addNum(self, num: int) -> None:
47+
if not len(self.max_heap):
48+
self.max_heap.append(-num)
49+
return
50+
51+
if num <= -self.max_heap[0]:
52+
heapq.heappush(self.max_heap, -num)
53+
else:
54+
heapq.heappush(self.min_heap, num)
55+
56+
if len(self.max_heap) > len(self.min_heap) + 1:
57+
e = heapq.heappop(self.max_heap)
58+
heapq.heappush(self.min_heap, -e)
59+
60+
if len(self.min_heap) > len(self.max_heap):
61+
e = heapq.heappop(self.min_heap)
62+
heapq.heappush(self.max_heap, -e)
63+
64+
def findMedian(self) -> float:
65+
if len(self.max_heap) == len(self.min_heap):
66+
return (-self.max_heap[0] + self.min_heap[0]) / 2
67+
else:
68+
return float(-self.max_heap[0])
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""
2+
295. Find Median from Data Stream
3+
4+
https://leetcode.com/problems/find-median-from-data-stream
5+
"""
6+
7+
from unittest import TestCase
8+
9+
from src.find_median_from_data_stream import MedianFinder
10+
11+
12+
class TestMedianFinder(TestCase):
13+
def test_1(self):
14+
mf = MedianFinder()
15+
mf.addNum(1)
16+
mf.addNum(2)
17+
assert mf.findMedian() == 1.5
18+
mf.addNum(3)
19+
assert mf.findMedian() == 2.0
20+
21+
def test_2(self):
22+
mf = MedianFinder()
23+
mf.addNum(6)
24+
assert mf.findMedian() == 6.0
25+
mf.addNum(10)
26+
assert mf.findMedian() == 8.0
27+
mf.addNum(2)
28+
assert mf.findMedian() == 6.0
29+
mf.addNum(6)
30+
assert mf.findMedian() == 6.0
31+
mf.addNum(5)
32+
assert mf.findMedian() == 6.0
33+
mf.addNum(0)
34+
assert mf.findMedian() == 5.5
35+
mf.addNum(6)
36+
assert mf.findMedian() == 6.0
37+
mf.addNum(3)
38+
assert mf.findMedian() == 5.5
39+
mf.addNum(1)
40+
assert mf.findMedian() == 5.0
41+
mf.addNum(0)
42+
assert mf.findMedian() == 4.0
43+
mf.addNum(0)
44+
assert mf.findMedian() == 3.0
45+
mf = MedianFinder()

0 commit comments

Comments
 (0)