diff --git a/src/main/java/com/thealgorithms/misc/MedianOfRunningArray.java b/src/main/java/com/thealgorithms/misc/MedianOfRunningArray.java index 62013ee31183..95f86f63f720 100644 --- a/src/main/java/com/thealgorithms/misc/MedianOfRunningArray.java +++ b/src/main/java/com/thealgorithms/misc/MedianOfRunningArray.java @@ -4,50 +4,74 @@ import java.util.PriorityQueue; /** - * @author shrutisheoran + * A generic abstract class to compute the median of a dynamically growing stream of numbers. + * + * @param the number type, must extend Number and be Comparable + * + * Usage: + * Extend this class and implement {@code calculateAverage(T a, T b)} to define how averaging is done. */ public abstract class MedianOfRunningArray> { - private PriorityQueue maxHeap; - private PriorityQueue minHeap; + private final PriorityQueue maxHeap; // Lower half (max-heap) + private final PriorityQueue minHeap; // Upper half (min-heap) - // Constructor public MedianOfRunningArray() { - this.maxHeap = new PriorityQueue<>(Collections.reverseOrder()); // Max Heap - this.minHeap = new PriorityQueue<>(); // Min Heap + this.maxHeap = new PriorityQueue<>(Collections.reverseOrder()); + this.minHeap = new PriorityQueue<>(); } - /* - Inserting lower half of array to max Heap - and upper half to min heap + /** + * Inserts a new number into the data structure. + * + * @param element the number to insert */ - public void insert(final T e) { - if (!minHeap.isEmpty() && e.compareTo(minHeap.peek()) < 0) { - maxHeap.offer(e); - if (maxHeap.size() > minHeap.size() + 1) { - minHeap.offer(maxHeap.poll()); - } + public final void insert(final T element) { + if (!minHeap.isEmpty() && element.compareTo(minHeap.peek()) < 0) { + maxHeap.offer(element); + balanceHeapsIfNeeded(); } else { - minHeap.offer(e); - if (minHeap.size() > maxHeap.size() + 1) { - maxHeap.offer(minHeap.poll()); - } + minHeap.offer(element); + balanceHeapsIfNeeded(); } } - /* - Returns median at any given point + /** + * Returns the median of the current elements. + * + * @return the median value + * @throws IllegalArgumentException if no elements have been inserted */ - public T median() { + public final T getMedian() { if (maxHeap.isEmpty() && minHeap.isEmpty()) { - throw new IllegalArgumentException("Enter at least 1 element, Median of empty list is not defined!"); - } else if (maxHeap.size() == minHeap.size()) { - T maxHeapTop = maxHeap.peek(); - T minHeapTop = minHeap.peek(); - return calculateAverage(maxHeapTop, minHeapTop); + throw new IllegalArgumentException("Median is undefined for an empty data set."); } - return maxHeap.size() > minHeap.size() ? maxHeap.peek() : minHeap.peek(); + + if (maxHeap.size() == minHeap.size()) { + return calculateAverage(maxHeap.peek(), minHeap.peek()); + } + + return (maxHeap.size() > minHeap.size()) ? maxHeap.peek() : minHeap.peek(); } - public abstract T calculateAverage(T a, T b); + /** + * Calculates the average between two values. + * Concrete subclasses must define how averaging works (e.g., for Integer, Double, etc.). + * + * @param a first number + * @param b second number + * @return the average of a and b + */ + protected abstract T calculateAverage(T a, T b); + + /** + * Balances the two heaps so that their sizes differ by at most 1. + */ + private void balanceHeapsIfNeeded() { + if (maxHeap.size() > minHeap.size() + 1) { + minHeap.offer(maxHeap.poll()); + } else if (minHeap.size() > maxHeap.size() + 1) { + maxHeap.offer(minHeap.poll()); + } + } } diff --git a/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java b/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java index e64ae1b741b6..f41953035846 100644 --- a/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java +++ b/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java @@ -11,12 +11,12 @@ */ public class MedianOfRunningArrayTest { - private static final String EXCEPTION_MESSAGE = "Enter at least 1 element, Median of empty list is not defined!"; + private static final String EXCEPTION_MESSAGE = "Median is undefined for an empty data set."; @Test public void testWhenInvalidInoutProvidedShouldThrowException() { var stream = new MedianOfRunningArrayInteger(); - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> stream.median()); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, stream::getMedian); assertEquals(exception.getMessage(), EXCEPTION_MESSAGE); } @@ -24,68 +24,68 @@ public void testWhenInvalidInoutProvidedShouldThrowException() { public void testWithNegativeValues() { var stream = new MedianOfRunningArrayInteger(); stream.insert(-1); - assertEquals(-1, stream.median()); + assertEquals(-1, stream.getMedian()); stream.insert(-2); - assertEquals(-1, stream.median()); + assertEquals(-1, stream.getMedian()); stream.insert(-3); - assertEquals(-2, stream.median()); + assertEquals(-2, stream.getMedian()); } @Test public void testWithSingleValues() { var stream = new MedianOfRunningArrayInteger(); stream.insert(-1); - assertEquals(-1, stream.median()); + assertEquals(-1, stream.getMedian()); } @Test public void testWithRandomValues() { var stream = new MedianOfRunningArrayInteger(); stream.insert(10); - assertEquals(10, stream.median()); + assertEquals(10, stream.getMedian()); stream.insert(5); - assertEquals(7, stream.median()); + assertEquals(7, stream.getMedian()); stream.insert(20); - assertEquals(10, stream.median()); + assertEquals(10, stream.getMedian()); stream.insert(15); - assertEquals(12, stream.median()); + assertEquals(12, stream.getMedian()); stream.insert(25); - assertEquals(15, stream.median()); + assertEquals(15, stream.getMedian()); stream.insert(30); - assertEquals(17, stream.median()); + assertEquals(17, stream.getMedian()); stream.insert(35); - assertEquals(20, stream.median()); + assertEquals(20, stream.getMedian()); stream.insert(1); - assertEquals(17, stream.median()); + assertEquals(17, stream.getMedian()); } @Test public void testWithNegativeAndPositiveValues() { var stream = new MedianOfRunningArrayInteger(); stream.insert(-1); - assertEquals(-1, stream.median()); + assertEquals(-1, stream.getMedian()); stream.insert(2); - assertEquals(0, stream.median()); + assertEquals(0, stream.getMedian()); stream.insert(-3); - assertEquals(-1, stream.median()); + assertEquals(-1, stream.getMedian()); } @Test public void testWithDuplicateValues() { var stream = new MedianOfRunningArrayInteger(); stream.insert(-1); - assertEquals(-1, stream.median()); + assertEquals(-1, stream.getMedian()); stream.insert(-1); - assertEquals(-1, stream.median()); + assertEquals(-1, stream.getMedian()); stream.insert(-1); - assertEquals(-1, stream.median()); + assertEquals(-1, stream.getMedian()); } @Test @@ -98,20 +98,20 @@ public void testWithDuplicateValuesB() { stream.insert(20); stream.insert(0); stream.insert(50); - assertEquals(10, stream.median()); + assertEquals(10, stream.getMedian()); } @Test public void testWithLargeValues() { var stream = new MedianOfRunningArrayInteger(); stream.insert(1000000); - assertEquals(1000000, stream.median()); + assertEquals(1000000, stream.getMedian()); stream.insert(12000); - assertEquals(506000, stream.median()); + assertEquals(506000, stream.getMedian()); stream.insert(15000000); - assertEquals(1000000, stream.median()); + assertEquals(1000000, stream.getMedian()); stream.insert(2300000); - assertEquals(1650000, stream.median()); + assertEquals(1650000, stream.getMedian()); } @Test @@ -120,7 +120,7 @@ public void testWithLargeCountOfValues() { for (int i = 1; i <= 1000; i++) { stream.insert(i); } - assertEquals(500, stream.median()); + assertEquals(500, stream.getMedian()); } @Test @@ -129,7 +129,7 @@ public void testWithThreeValuesInDescendingOrder() { stream.insert(30); stream.insert(20); stream.insert(10); - assertEquals(20, stream.median()); + assertEquals(20, stream.getMedian()); } @Test @@ -138,7 +138,7 @@ public void testWithThreeValuesInOrder() { stream.insert(10); stream.insert(20); stream.insert(30); - assertEquals(20, stream.median()); + assertEquals(20, stream.getMedian()); } @Test @@ -147,7 +147,7 @@ public void testWithThreeValuesNotInOrderA() { stream.insert(30); stream.insert(10); stream.insert(20); - assertEquals(20, stream.median()); + assertEquals(20, stream.getMedian()); } @Test @@ -156,46 +156,46 @@ public void testWithThreeValuesNotInOrderB() { stream.insert(20); stream.insert(10); stream.insert(30); - assertEquals(20, stream.median()); + assertEquals(20, stream.getMedian()); } @Test public void testWithFloatValues() { var stream = new MedianOfRunningArrayFloat(); stream.insert(20.0f); - assertEquals(20.0f, stream.median()); + assertEquals(20.0f, stream.getMedian()); stream.insert(10.5f); - assertEquals(15.25f, stream.median()); + assertEquals(15.25f, stream.getMedian()); stream.insert(30.0f); - assertEquals(20.0f, stream.median()); + assertEquals(20.0f, stream.getMedian()); } @Test public void testWithByteValues() { var stream = new MedianOfRunningArrayByte(); stream.insert((byte) 120); - assertEquals((byte) 120, stream.median()); + assertEquals((byte) 120, stream.getMedian()); stream.insert((byte) -120); - assertEquals((byte) 0, stream.median()); + assertEquals((byte) 0, stream.getMedian()); stream.insert((byte) 127); - assertEquals((byte) 120, stream.median()); + assertEquals((byte) 120, stream.getMedian()); } @Test public void testWithLongValues() { var stream = new MedianOfRunningArrayLong(); stream.insert(120000000L); - assertEquals(120000000L, stream.median()); + assertEquals(120000000L, stream.getMedian()); stream.insert(92233720368547757L); - assertEquals(46116860244273878L, stream.median()); + assertEquals(46116860244273878L, stream.getMedian()); } @Test public void testWithDoubleValues() { var stream = new MedianOfRunningArrayDouble(); stream.insert(12345.67891); - assertEquals(12345.67891, stream.median()); + assertEquals(12345.67891, stream.getMedian()); stream.insert(23456789.98); - assertEquals(11734567.83, stream.median(), .01); + assertEquals(11734567.83, stream.getMedian(), .01); } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy