From dc9d377191fff551d55e75d0601cca22571ae9dd Mon Sep 17 00:00:00 2001 From: LUKMANul Date: Mon, 6 Oct 2025 00:20:23 +0530 Subject: [PATCH 1/8] Add deterministic QuickSelect (Median of Medians) --- .../DeterministicQuickSelect.java | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/main/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.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..4ce2df7277df --- /dev/null +++ b/src/main/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java @@ -0,0 +1,114 @@ +package com.thealgorithms.divideandconquer; + +import java.util.Arrays; + +/** + * QuickSelect algorithm using the Median of Medians method. + * + *

This algorithm finds the kth smallest element in an unsorted array in + * O(n) worst-case time complexity. + * + *

Steps: + *

    + *
  1. Divide the array into groups of five elements each.
  2. + *
  3. Find the median of each group.
  4. + *
  5. Recursively find the median of these medians, which becomes the pivot.
  6. + *
  7. Partition the array around this pivot.
  8. + *
  9. Recurse into the part that contains the kth smallest element.
  10. + *
+ * + *

Reference: + * + * Median of Medians Algorithm + */ +public final class QuickSelectMedianOfMedians { + + private QuickSelectMedianOfMedians() { + // Utility class; prevent instantiation + } + + /** + * Returns the kth smallest element in the given array using the + * deterministic Median of Medians approach. + * + * @param arr the input array + * @param k index (0-based) of the kth smallest element to find + * @return the kth smallest element + * @throws IllegalArgumentException if input is invalid + */ + public static int quickSelect(int[] arr, int k) { + if (arr == null || arr.length == 0 || k < 0 || k >= arr.length) { + throw new IllegalArgumentException("Invalid input"); + } + return select(arr, 0, arr.length - 1, k); + } + + private static int select(int[] arr, int left, int right, int k) { + if (left == right) { + return arr[left]; + } + + int pivotIndex = getPivotIndex(arr, left, right); + int pivotValue = arr[pivotIndex]; + int partitionIndex = partition(arr, left, right, pivotValue); + + if (k == partitionIndex) { + return arr[k]; + } else if (k < partitionIndex) { + return select(arr, left, partitionIndex - 1, k); + } else { + return select(arr, partitionIndex + 1, right, k); + } + } + + private static int getPivotIndex(int[] arr, int left, int right) { + int n = right - left + 1; + if (n < 5) { + Arrays.sort(arr, left, right + 1); + return left + n / 2; + } + + int numMedians = (int) Math.ceil(n / 5.0); + int[] medians = new int[numMedians]; + + for (int i = 0; i < numMedians; i++) { + int subLeft = left + i * 5; + int subRight = Math.min(subLeft + 4, right); + Arrays.sort(arr, subLeft, subRight + 1); + medians[i] = arr[subLeft + (subRight - subLeft) / 2]; + } + + int medianOfMedians = quickSelect(medians, numMedians / 2); + for (int i = left; i <= right; i++) { + if (arr[i] == medianOfMedians) { + return i; + } + } + return left; // fallback + } + + private static int partition(int[] arr, int left, int right, int pivotValue) { + int i = left; + for (int j = left; j <= right; j++) { + if (arr[j] < pivotValue) { + swap(arr, i, j); + i++; + } + } + + int pivotIndex = i; + for (int j = i; j <= right; j++) { + if (arr[j] == pivotValue) { + swap(arr, j, pivotIndex); + break; + } + } + return pivotIndex; + } + + private static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +} From 05d6dccccf60cad9b5c7d1f8ff83a5c2f7b7d3cb Mon Sep 17 00:00:00 2001 From: LUKMANul Date: Mon, 6 Oct 2025 00:22:19 +0530 Subject: [PATCH 2/8] Add JUnit tests for QuickSelectMedianOfMedians --- .../DeterministicQuickSelectTest.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java 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..4d3b8c44ca37 --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java @@ -0,0 +1,32 @@ +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; + +public class QuickSelectMedianOfMediansTest { + + @Test + public void testBasicCases() { + assertEquals(7, QuickSelectMedianOfMedians.quickSelect(new int[]{12, 3, 5, 7, 4, 19, 26}, 3)); + assertEquals(4, QuickSelectMedianOfMedians.quickSelect(new int[]{4, 2, 5, 2, 7, 4, 3}, 4)); + assertEquals(0, QuickSelectMedianOfMedians.quickSelect(new int[]{-5, -10, 0, 5, 10}, 2)); + assertEquals(3, QuickSelectMedianOfMedians.quickSelect(new int[]{8, 3, 1}, 1)); + assertEquals(10, QuickSelectMedianOfMedians.quickSelect(new int[]{10, 20, 30, 40, 50}, 0)); + assertEquals(50, QuickSelectMedianOfMedians.quickSelect(new int[]{10, 20, 30, 40, 50}, 4)); + assertEquals(42, QuickSelectMedianOfMedians.quickSelect(new int[]{42}, 0)); + } + + @Test + public void testInvalidInputs() { + assertThrows(IllegalArgumentException.class, () -> + QuickSelectMedianOfMedians.quickSelect(null, 0)); + assertThrows(IllegalArgumentException.class, () -> + QuickSelectMedianOfMedians.quickSelect(new int[]{1, 2, 3}, -1)); + assertThrows(IllegalArgumentException.class, () -> + QuickSelectMedianOfMedians.quickSelect(new int[]{1, 2, 3}, 3)); + assertThrows(IllegalArgumentException.class, () -> + QuickSelectMedianOfMedians.quickSelect(new int[]{}, 0)); + } +} From a48da202b90280c010bed894b4af29f75738c8d9 Mon Sep 17 00:00:00 2001 From: LUKMANul Date: Mon, 6 Oct 2025 00:51:15 +0530 Subject: [PATCH 3/8] Fix clang-format style for DeterministicQuickSelectTest --- .../DeterministicQuickSelectTest.java | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java index 4d3b8c44ca37..f4d78889567d 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java @@ -1,32 +1,27 @@ 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; -public class QuickSelectMedianOfMediansTest { +public final class DeterministicQuickSelectTest { @Test public void testBasicCases() { - assertEquals(7, QuickSelectMedianOfMedians.quickSelect(new int[]{12, 3, 5, 7, 4, 19, 26}, 3)); - assertEquals(4, QuickSelectMedianOfMedians.quickSelect(new int[]{4, 2, 5, 2, 7, 4, 3}, 4)); - assertEquals(0, QuickSelectMedianOfMedians.quickSelect(new int[]{-5, -10, 0, 5, 10}, 2)); - assertEquals(3, QuickSelectMedianOfMedians.quickSelect(new int[]{8, 3, 1}, 1)); - assertEquals(10, QuickSelectMedianOfMedians.quickSelect(new int[]{10, 20, 30, 40, 50}, 0)); - assertEquals(50, QuickSelectMedianOfMedians.quickSelect(new int[]{10, 20, 30, 40, 50}, 4)); - assertEquals(42, QuickSelectMedianOfMedians.quickSelect(new int[]{42}, 0)); + assertEquals(7, QuickSelectMedianOfMedians.quickSelect(new int[] {12, 3, 5, 7, 4, 19, 26}, 3)); + assertEquals(4, QuickSelectMedianOfMedians.quickSelect(new int[] {4, 2, 5, 2, 7, 4, 3}, 4)); + assertEquals(0, QuickSelectMedianOfMedians.quickSelect(new int[] {-5, -10, 0, 5, 10}, 2)); + assertEquals(3, QuickSelectMedianOfMedians.quickSelect(new int[] {8, 3, 1}, 1)); + assertEquals(10, QuickSelectMedianOfMedians.quickSelect(new int[] {10, 20, 30, 40, 50}, 0)); + assertEquals(50, QuickSelectMedianOfMedians.quickSelect(new int[] {10, 20, 30, 40, 50}, 4)); + assertEquals(42, QuickSelectMedianOfMedians.quickSelect(new int[] {42}, 0)); } @Test public void testInvalidInputs() { - assertThrows(IllegalArgumentException.class, () -> - QuickSelectMedianOfMedians.quickSelect(null, 0)); - assertThrows(IllegalArgumentException.class, () -> - QuickSelectMedianOfMedians.quickSelect(new int[]{1, 2, 3}, -1)); - assertThrows(IllegalArgumentException.class, () -> - QuickSelectMedianOfMedians.quickSelect(new int[]{1, 2, 3}, 3)); - assertThrows(IllegalArgumentException.class, () -> - QuickSelectMedianOfMedians.quickSelect(new int[]{}, 0)); + assertThrows(IllegalArgumentException.class, () -> QuickSelectMedianOfMedians.quickSelect(null, 0)); + assertThrows(IllegalArgumentException.class, () -> QuickSelectMedianOfMedians.quickSelect(new int[] {1, 2, 3}, -1)); + assertThrows(IllegalArgumentException.class, () -> QuickSelectMedianOfMedians.quickSelect(new int[] {1, 2, 3}, 3)); + assertThrows(IllegalArgumentException.class, () -> QuickSelectMedianOfMedians.quickSelect(new int[] {}, 0)); } } From d47fde1704667b8a92c370cdfc44b576a900dc85 Mon Sep 17 00:00:00 2001 From: LUKMANul Date: Mon, 6 Oct 2025 00:59:58 +0530 Subject: [PATCH 4/8] Fix: Rename QuickSelect file to match public class name --- ...erministicQuickSelect.java => QuickSelectMedianOfMedians.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/com/thealgorithms/divideandconquer/{DeterministicQuickSelect.java => QuickSelectMedianOfMedians.java} (100%) diff --git a/src/main/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java b/src/main/java/com/thealgorithms/divideandconquer/QuickSelectMedianOfMedians.java similarity index 100% rename from src/main/java/com/thealgorithms/divideandconquer/DeterministicQuickSelect.java rename to src/main/java/com/thealgorithms/divideandconquer/QuickSelectMedianOfMedians.java From 29202998acc6ab300fe8cb1671bb3d30d5caaa22 Mon Sep 17 00:00:00 2001 From: LUKMANul Date: Mon, 6 Oct 2025 01:09:58 +0530 Subject: [PATCH 5/8] Fix(test): Resolve Checkstyle and compilation errors in QuickSelectTest Updated imports in DeterministicQuickSelectTest.java to explicitly list JUnit methods, resolving the 'AvoidStarImport' Checkstyle rule. Also re-added the missing 'org.junit.jupiter.api.Test' import. --- .../divideandconquer/DeterministicQuickSelectTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java index f4d78889567d..665b91897e99 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java @@ -1,9 +1,11 @@ package com.thealgorithms.divideandconquer; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; // Explicitly importing assertEquals +import static org.junit.jupiter.api.Assertions.assertThrows; // Explicitly importing assertThrows import org.junit.jupiter.api.Test; + public final class DeterministicQuickSelectTest { @Test From 7d0266002208258b9508e65cf821e8d1091e0e28 Mon Sep 17 00:00:00 2001 From: LUKMANul Date: Mon, 6 Oct 2025 01:17:14 +0530 Subject: [PATCH 6/8] Style(test): Reorder imports in QuickSelectTest for lint compliance Adjusted the import order in DeterministicQuickSelectTest.java, placing regular imports before static imports, to satisfy the project's formatting and linting rules. --- .../divideandconquer/DeterministicQuickSelectTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java index 665b91897e99..01559d5df28a 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java @@ -1,10 +1,9 @@ package com.thealgorithms.divideandconquer; -import static org.junit.jupiter.api.Assertions.assertEquals; // Explicitly importing assertEquals -import static org.junit.jupiter.api.Assertions.assertThrows; // Explicitly importing assertThrows - -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Test; // Regular imports come first +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public final class DeterministicQuickSelectTest { @@ -27,3 +26,4 @@ public void testInvalidInputs() { assertThrows(IllegalArgumentException.class, () -> QuickSelectMedianOfMedians.quickSelect(new int[] {}, 0)); } } + From dbd34ea94889338f7b6f67dbcf14dc706e8e60cc Mon Sep 17 00:00:00 2001 From: LUKMANul Date: Mon, 6 Oct 2025 01:22:47 +0530 Subject: [PATCH 7/8] Style(test): Final import cleanup for lint compliance Removed trailing comments on import statements and ensured correct ordering (regular before static) in DeterministicQuickSelectTest.java to pass the project's formatting checks. --- .../divideandconquer/DeterministicQuickSelectTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java index 01559d5df28a..b048e374ec30 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java @@ -1,6 +1,6 @@ package com.thealgorithms.divideandconquer; -import org.junit.jupiter.api.Test; // Regular imports come first +import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -26,4 +26,3 @@ public void testInvalidInputs() { assertThrows(IllegalArgumentException.class, () -> QuickSelectMedianOfMedians.quickSelect(new int[] {}, 0)); } } - From dff12ad3f5c8aabc965963552cbd1821ee994e22 Mon Sep 17 00:00:00 2001 From: LUKMANul Date: Mon, 6 Oct 2025 01:24:40 +0530 Subject: [PATCH 8/8] issue fixed. --- .../divideandconquer/DeterministicQuickSelectTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java index b048e374ec30..b8260e118fd8 100644 --- a/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java +++ b/src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java @@ -1,10 +1,10 @@ package com.thealgorithms.divideandconquer; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; + public final class DeterministicQuickSelectTest { @Test