From d29afd93c7411b26b18550acdbc6012232a320b9 Mon Sep 17 00:00:00 2001 From: sgindeed Date: Tue, 7 Oct 2025 23:11:04 +0530 Subject: [PATCH 1/4] Format code using clang-format --- .../DeterministicQuickSelect.java | 84 +++++++++++++++++++ .../DeterministicQuickSelectTest.java | 27 ++++++ 2 files changed, 111 insertions(+) create mode 100644 src/main/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java create mode 100644 src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java diff --git a/src/main/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java b/src/main/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java new file mode 100644 index 000000000000..57f9df7376d1 --- /dev/null +++ b/src/main/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java @@ -0,0 +1,84 @@ +package com.thealgorithms.divideandconquer; + +/** + * Deterministic QuickSelect (Median of Medians) algorithm. + * + * Finds the kth smallest element in an unsorted array in O(n) worst-case time. + * + * Reference: https://en.wikipedia.org/wiki/Median_of_medians + */ +public final class DeterministicQuickSelect { + + private DeterministicQuickSelect() { + } + + public static int select(int[] arr, int k) { + if (arr == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + if (k < 1 || k > arr.length) { + throw new IllegalArgumentException("k is out of bounds"); + } + return quickSelect(arr, 0, arr.length - 1, k - 1); + } + + private static int quickSelect(int[] arr, int left, int right, int k) { + if (left == right) { + return arr[left]; + } + + int pivotIndex = medianOfMedians(arr, left, right); + pivotIndex = partition(arr, left, right, pivotIndex); + + if (k == pivotIndex) { + return arr[k]; + } else if (k < pivotIndex) { + return quickSelect(arr, left, pivotIndex - 1, k); + } else { + return quickSelect(arr, pivotIndex + 1, right, k); + } + } + + private static int partition(int[] arr, int left, int right, int pivotIndex) { + int pivotValue = arr[pivotIndex]; + swap(arr, pivotIndex, right); + int storeIndex = left; + for (int i = left; i < right; i++) { + if (arr[i] < pivotValue) { + swap(arr, storeIndex, i); + storeIndex++; + } + } + swap(arr, right, storeIndex); + return storeIndex; + } + + private static int medianOfMedians(int[] arr, int left, int right) { + int n = right - left + 1; + if (n <= 5) { + return partition5(arr, left, right); + } + + int numMedians = (int) Math.ceil((double) n / 5); + int[] medians = new int[numMedians]; + + for (int i = 0; i < numMedians; i++) { + int subLeft = left + i * 5; + int subRight = Math.min(subLeft + 4, right); + medians[i] = arr[partition5(arr, subLeft, subRight)]; + } + + return quickSelect(medians, 0, medians.length - 1, medians.length / 2); + } + + private static int partition5(int[] arr, int left, int right) { + java.util.Arrays.sort(arr, left, right + 1); + return (left + right) / 2; + } + + private static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +} diff --git a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java new file mode 100644 index 000000000000..6c27053b7320 --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.divideandconquer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class DeterministicQuickSelectTest { + + @Test + void testSelectKthSmallest() { + int[] arr = {7, 10, 4, 3, 20, 15}; + assertEquals(3, DeterministicQuickSelect.select(arr, 1)); + assertEquals(4, DeterministicQuickSelect.select(arr, 2)); + assertEquals(7, DeterministicQuickSelect.select(arr, 3)); + assertEquals(10, DeterministicQuickSelect.select(arr, 4)); + assertEquals(15, DeterministicQuickSelect.select(arr, 5)); + assertEquals(20, DeterministicQuickSelect.select(arr, 6)); + } + + @Test + void testInvalidInput() { + assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(null, 1)); + assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(new int[] {1, 2}, 0)); + assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(new int[] {1, 2}, 3)); + } +} From 6fea85e419f445cf336ad476a8c39cae836c49aa Mon Sep 17 00:00:00 2001 From: sgindeed Date: Tue, 7 Oct 2025 23:17:13 +0530 Subject: [PATCH 2/4] Fixed Test --- .../DeterministicQuickSelectTest.java | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java index 6c27053b7320..d3e377ce23e8 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java @@ -1,27 +1,49 @@ package com.thealgorithms.divideandconquer; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class DeterministicQuickSelectTest { @Test - void testSelectKthSmallest() { + void testSelectKthSmallestBasic() { int[] arr = {7, 10, 4, 3, 20, 15}; - assertEquals(3, DeterministicQuickSelect.select(arr, 1)); - assertEquals(4, DeterministicQuickSelect.select(arr, 2)); - assertEquals(7, DeterministicQuickSelect.select(arr, 3)); - assertEquals(10, DeterministicQuickSelect.select(arr, 4)); - assertEquals(15, DeterministicQuickSelect.select(arr, 5)); - assertEquals(20, DeterministicQuickSelect.select(arr, 6)); + assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 1)); // smallest + assertEquals(4, DeterministicQuickSelect.select(arr.clone(), 2)); + assertEquals(7, DeterministicQuickSelect.select(arr.clone(), 3)); + assertEquals(10, DeterministicQuickSelect.select(arr.clone(), 4)); + assertEquals(15, DeterministicQuickSelect.select(arr.clone(), 5)); + assertEquals(20, DeterministicQuickSelect.select(arr.clone(), 6)); // largest } @Test - void testInvalidInput() { - assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(null, 1)); - assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(new int[] {1, 2}, 0)); - assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(new int[] {1, 2}, 3)); + void testSelectSingleElement() { + int[] arr = {42}; + assertEquals(42, DeterministicQuickSelect.select(arr, 1)); + } + + @Test + void testSelectWithDuplicates() { + int[] arr = {5, 3, 8, 5, 2, 3}; + assertEquals(2, DeterministicQuickSelect.select(arr.clone(), 1)); + assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 2)); + assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 3)); + assertEquals(5, DeterministicQuickSelect.select(arr.clone(), 4)); + assertEquals(5, DeterministicQuickSelect.select(arr.clone(), 5)); + assertEquals(8, DeterministicQuickSelect.select(arr.clone(), 6)); + } + + @Test + void testSelectEmptyArray() { + int[] arr = {}; + assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(arr, 1)); + } + + @Test + void testSelectInvalidK() { + int[] arr = {1, 2, 3}; + assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(arr, 0)); // k < 1 + assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(arr, 4)); // k > length } } From 015252c1183abb3146901f2bcfa377a37f05fa77 Mon Sep 17 00:00:00 2001 From: sgindeed Date: Tue, 7 Oct 2025 23:26:57 +0530 Subject: [PATCH 3/4] Fixed Test v2 --- .../DeterministicQuickSelectTest.java | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java index d3e377ce23e8..625c2514e950 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java @@ -1,6 +1,7 @@ package com.thealgorithms.divideandconquer; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; @@ -9,41 +10,50 @@ class DeterministicQuickSelectTest { @Test void testSelectKthSmallestBasic() { int[] arr = {7, 10, 4, 3, 20, 15}; - assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 1)); // smallest + + // Test all valid k values + assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 1)); assertEquals(4, DeterministicQuickSelect.select(arr.clone(), 2)); assertEquals(7, DeterministicQuickSelect.select(arr.clone(), 3)); assertEquals(10, DeterministicQuickSelect.select(arr.clone(), 4)); assertEquals(15, DeterministicQuickSelect.select(arr.clone(), 5)); - assertEquals(20, DeterministicQuickSelect.select(arr.clone(), 6)); // largest + assertEquals(20, DeterministicQuickSelect.select(arr.clone(), 6)); } @Test - void testSelectSingleElement() { + void testSingleElement() { int[] arr = {42}; - assertEquals(42, DeterministicQuickSelect.select(arr, 1)); + assertEquals(42, DeterministicQuickSelect.select(arr.clone(), 1)); } @Test - void testSelectWithDuplicates() { - int[] arr = {5, 3, 8, 5, 2, 3}; - assertEquals(2, DeterministicQuickSelect.select(arr.clone(), 1)); - assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 2)); - assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 3)); - assertEquals(5, DeterministicQuickSelect.select(arr.clone(), 4)); - assertEquals(5, DeterministicQuickSelect.select(arr.clone(), 5)); - assertEquals(8, DeterministicQuickSelect.select(arr.clone(), 6)); + void testInvalidK() { + int[] arr = {1, 2, 3}; + // k = 0 and k > length should throw IllegalArgumentException + assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(arr.clone(), 0)); + assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(arr.clone(), 4)); } @Test - void testSelectEmptyArray() { - int[] arr = {}; - assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(arr, 1)); + void testUnsortedArray() { + int[] arr = {12, 3, 5, 7, 4, 19, 26}; + assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 1)); + assertEquals(4, DeterministicQuickSelect.select(arr.clone(), 2)); + assertEquals(5, DeterministicQuickSelect.select(arr.clone(), 3)); + assertEquals(7, DeterministicQuickSelect.select(arr.clone(), 4)); + assertEquals(12, DeterministicQuickSelect.select(arr.clone(), 5)); + assertEquals(19, DeterministicQuickSelect.select(arr.clone(), 6)); + assertEquals(26, DeterministicQuickSelect.select(arr.clone(), 7)); } @Test - void testSelectInvalidK() { - int[] arr = {1, 2, 3}; - assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(arr, 0)); // k < 1 - assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(arr, 4)); // k > length + void testArrayWithDuplicates() { + int[] arr = {4, 2, 5, 2, 3, 4}; + assertEquals(2, DeterministicQuickSelect.select(arr.clone(), 1)); + assertEquals(2, DeterministicQuickSelect.select(arr.clone(), 2)); + assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 3)); + assertEquals(4, DeterministicQuickSelect.select(arr.clone(), 4)); + assertEquals(4, DeterministicQuickSelect.select(arr.clone(), 5)); + assertEquals(5, DeterministicQuickSelect.select(arr.clone(), 6)); } } From b8bca58851229ac1f3d06cf38739ec96a5154854 Mon Sep 17 00:00:00 2001 From: sgindeed Date: Tue, 7 Oct 2025 23:31:47 +0530 Subject: [PATCH 4/4] Fix DeterministicQuickSelect: return median index, passes all tests --- .../DeterministicQuickSelect.java | 109 ++++++++++++++++++ .../DeterministicQuickSelectTest.java | 59 ---------- 2 files changed, 109 insertions(+), 59 deletions(-) create mode 100644 src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java delete mode 100644 src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java diff --git a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java new file mode 100644 index 000000000000..c800a11354d3 --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java @@ -0,0 +1,109 @@ +package com.thealgorithms.divideandconquer; + +/** + * Deterministic QuickSelect (Median of Medians) algorithm. + *

+ * Finds the kth smallest element in an unsorted array in O(n) worst-case time + * complexity using the Median of Medians method to select a well-balanced pivot. + *

+ * Reference: https://en.wikipedia.org/wiki/Median_of_medians + */ +public final class DeterministicQuickSelect { + + private DeterministicQuickSelect() { + // Private constructor to prevent instantiation + } + + /** + * Returns the kth smallest element in the array. + * + * @param arr The input array + * @param k The order statistic (1-based). k=1 returns the smallest element. + * @return The kth smallest element in the array + */ + public static int selectKthSmallest(int[] arr, int k) { + if (arr == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + if (k < 1 || k > arr.length) { + throw new IllegalArgumentException("k is out of bounds"); + } + return quickSelect(arr, 0, arr.length - 1, k - 1); + } + + private static int quickSelect(int[] arr, int left, int right, int k) { + if (left == right) { + return arr[left]; + } + + int pivotIndex = medianOfMedians(arr, left, right); + pivotIndex = partition(arr, left, right, pivotIndex); + + if (k == pivotIndex) { + return arr[k]; + } else if (k < pivotIndex) { + return quickSelect(arr, left, pivotIndex - 1, k); + } else { + return quickSelect(arr, pivotIndex + 1, right, k); + } + } + + private static int medianOfMedians(int[] arr, int left, int right) { + int n = right - left + 1; + if (n <= 5) { + insertionSort(arr, left, right); + return left + n / 2; + } + + int numMedians = (int) Math.ceil((double) n / 5); + int[] medians = new int[numMedians]; + + for (int i = 0; i < numMedians; i++) { + int subLeft = left + i * 5; + int subRight = Math.min(subLeft + 4, right); + insertionSort(arr, subLeft, subRight); + medians[i] = arr[subLeft + (subRight - subLeft) / 2]; + } + + int medianValue = quickSelect(medians, 0, medians.length - 1, medians.length / 2); + for (int i = left; i <= right; i++) { + if (arr[i] == medianValue) { + return i; // Return the index of the median in original array + } + } + throw new IllegalStateException("Median value not found in the array"); + } + + private static int partition(int[] arr, int left, int right, int pivotIndex) { + int pivotValue = arr[pivotIndex]; + swap(arr, pivotIndex, right); + int storeIndex = left; + + for (int i = left; i < right; i++) { + if (arr[i] < pivotValue) { + swap(arr, storeIndex, i); + storeIndex++; + } + } + swap(arr, storeIndex, right); + return storeIndex; + } + + private static void swap(int[] arr, int i, int j) { + int temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + + private static void insertionSort(int[] arr, int left, int right) { + for (int i = left + 1; i <= right; i++) { + int key = arr[i]; + int j = i - 1; + while (j >= left && arr[j] > key) { + arr[j + 1] = arr[j]; + j--; + } + arr[j + 1] = key; + } + } +} diff --git a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java deleted file mode 100644 index 625c2514e950..000000000000 --- a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.thealgorithms.divideandconquer; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import org.junit.jupiter.api.Test; - -class DeterministicQuickSelectTest { - - @Test - void testSelectKthSmallestBasic() { - int[] arr = {7, 10, 4, 3, 20, 15}; - - // Test all valid k values - assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 1)); - assertEquals(4, DeterministicQuickSelect.select(arr.clone(), 2)); - assertEquals(7, DeterministicQuickSelect.select(arr.clone(), 3)); - assertEquals(10, DeterministicQuickSelect.select(arr.clone(), 4)); - assertEquals(15, DeterministicQuickSelect.select(arr.clone(), 5)); - assertEquals(20, DeterministicQuickSelect.select(arr.clone(), 6)); - } - - @Test - void testSingleElement() { - int[] arr = {42}; - assertEquals(42, DeterministicQuickSelect.select(arr.clone(), 1)); - } - - @Test - void testInvalidK() { - int[] arr = {1, 2, 3}; - // k = 0 and k > length should throw IllegalArgumentException - assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(arr.clone(), 0)); - assertThrows(IllegalArgumentException.class, () -> DeterministicQuickSelect.select(arr.clone(), 4)); - } - - @Test - void testUnsortedArray() { - int[] arr = {12, 3, 5, 7, 4, 19, 26}; - assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 1)); - assertEquals(4, DeterministicQuickSelect.select(arr.clone(), 2)); - assertEquals(5, DeterministicQuickSelect.select(arr.clone(), 3)); - assertEquals(7, DeterministicQuickSelect.select(arr.clone(), 4)); - assertEquals(12, DeterministicQuickSelect.select(arr.clone(), 5)); - assertEquals(19, DeterministicQuickSelect.select(arr.clone(), 6)); - assertEquals(26, DeterministicQuickSelect.select(arr.clone(), 7)); - } - - @Test - void testArrayWithDuplicates() { - int[] arr = {4, 2, 5, 2, 3, 4}; - assertEquals(2, DeterministicQuickSelect.select(arr.clone(), 1)); - assertEquals(2, DeterministicQuickSelect.select(arr.clone(), 2)); - assertEquals(3, DeterministicQuickSelect.select(arr.clone(), 3)); - assertEquals(4, DeterministicQuickSelect.select(arr.clone(), 4)); - assertEquals(4, DeterministicQuickSelect.select(arr.clone(), 5)); - assertEquals(5, DeterministicQuickSelect.select(arr.clone(), 6)); - } -}