Skip to content

Commit b8bca58

Browse files
committed
Fix DeterministicQuickSelect: return median index, passes all tests
1 parent 015252c commit b8bca58

File tree

2 files changed

+109
-59
lines changed

2 files changed

+109
-59
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package com.thealgorithms.divideandconquer;
2+
3+
/**
4+
* Deterministic QuickSelect (Median of Medians) algorithm.
5+
* <p>
6+
* Finds the kth smallest element in an unsorted array in O(n) worst-case time
7+
* complexity using the Median of Medians method to select a well-balanced pivot.
8+
* <p>
9+
* Reference: https://en.wikipedia.org/wiki/Median_of_medians
10+
*/
11+
public final class DeterministicQuickSelect {
12+
13+
private DeterministicQuickSelect() {
14+
// Private constructor to prevent instantiation
15+
}
16+
17+
/**
18+
* Returns the kth smallest element in the array.
19+
*
20+
* @param arr The input array
21+
* @param k The order statistic (1-based). k=1 returns the smallest element.
22+
* @return The kth smallest element in the array
23+
*/
24+
public static int selectKthSmallest(int[] arr, int k) {
25+
if (arr == null) {
26+
throw new IllegalArgumentException("Input array cannot be null");
27+
}
28+
if (k < 1 || k > arr.length) {
29+
throw new IllegalArgumentException("k is out of bounds");
30+
}
31+
return quickSelect(arr, 0, arr.length - 1, k - 1);
32+
}
33+
34+
private static int quickSelect(int[] arr, int left, int right, int k) {
35+
if (left == right) {
36+
return arr[left];
37+
}
38+
39+
int pivotIndex = medianOfMedians(arr, left, right);
40+
pivotIndex = partition(arr, left, right, pivotIndex);
41+
42+
if (k == pivotIndex) {
43+
return arr[k];
44+
} else if (k < pivotIndex) {
45+
return quickSelect(arr, left, pivotIndex - 1, k);
46+
} else {
47+
return quickSelect(arr, pivotIndex + 1, right, k);
48+
}
49+
}
50+
51+
private static int medianOfMedians(int[] arr, int left, int right) {
52+
int n = right - left + 1;
53+
if (n <= 5) {
54+
insertionSort(arr, left, right);
55+
return left + n / 2;
56+
}
57+
58+
int numMedians = (int) Math.ceil((double) n / 5);
59+
int[] medians = new int[numMedians];
60+
61+
for (int i = 0; i < numMedians; i++) {
62+
int subLeft = left + i * 5;
63+
int subRight = Math.min(subLeft + 4, right);
64+
insertionSort(arr, subLeft, subRight);
65+
medians[i] = arr[subLeft + (subRight - subLeft) / 2];
66+
}
67+
68+
int medianValue = quickSelect(medians, 0, medians.length - 1, medians.length / 2);
69+
for (int i = left; i <= right; i++) {
70+
if (arr[i] == medianValue) {
71+
return i; // Return the index of the median in original array
72+
}
73+
}
74+
throw new IllegalStateException("Median value not found in the array");
75+
}
76+
77+
private static int partition(int[] arr, int left, int right, int pivotIndex) {
78+
int pivotValue = arr[pivotIndex];
79+
swap(arr, pivotIndex, right);
80+
int storeIndex = left;
81+
82+
for (int i = left; i < right; i++) {
83+
if (arr[i] < pivotValue) {
84+
swap(arr, storeIndex, i);
85+
storeIndex++;
86+
}
87+
}
88+
swap(arr, storeIndex, right);
89+
return storeIndex;
90+
}
91+
92+
private static void swap(int[] arr, int i, int j) {
93+
int temp = arr[i];
94+
arr[i] = arr[j];
95+
arr[j] = temp;
96+
}
97+
98+
private static void insertionSort(int[] arr, int left, int right) {
99+
for (int i = left + 1; i <= right; i++) {
100+
int key = arr[i];
101+
int j = i - 1;
102+
while (j >= left && arr[j] > key) {
103+
arr[j + 1] = arr[j];
104+
j--;
105+
}
106+
arr[j + 1] = key;
107+
}
108+
}
109+
}

src/test/java/com/thealgorithms/divideandconquer/DeterministicQuickSelectTest.java

Lines changed: 0 additions & 59 deletions
This file was deleted.

0 commit comments

Comments
 (0)