1
1
package src .algorithms .sorting .quickSort .threeWayPartitioning ;
2
2
3
3
/**
4
- * Here, we are implementing QuickSort with three-way partitioning where we sort the array in increasing (or more
5
- * precisely, non-decreasing) order.
4
+ * Here, we are implementing Paranoid QuickSort with three-way partitioning where we sort the array in increasing (or
5
+ * more precisely, non-decreasing) order.
6
6
*
7
7
* Three-way partitioning is used in QuickSort to tackle the scenario where there are many duplicate elements in the
8
8
* array being sorted.
17
17
*
18
18
* Complexity Analysis:
19
19
* Time:
20
- * - Worst case (poor choice of pivot) : O(n^2 )
20
+ * - Worst case: O(nlogn )
21
21
* - Average case: O(nlogn)
22
22
* - Best case: O(nlogn)
23
23
*
24
24
* By isolating the elements equal to the pivot into their correct positions during the partitioning step, three-way
25
25
* partitioning efficiently handles duplicates, preventing the presence of many duplicates in the array from causing
26
26
* the time complexity of QuickSort to degrade to O(n^2).
27
27
*
28
- * In the worst case where the pivot selected is consistently the smallest or biggest element in the array, the
29
- * partitioning of the array around the pivot will be extremely unbalanced, leading to a recurrence relation of:
30
- * T(n) = T(n-1) + O(n) => O(n^2). However, the likelihood of this happening is extremely low since pivot selection is
31
- * randomised.
32
- *
33
28
* Space:
34
29
* - O(1) since sorting is done in-place
35
30
*/
@@ -56,9 +51,13 @@ public static void sort(int[] arr) {
56
51
public static void quickSort (int [] arr , int start , int end ) {
57
52
if (start < end ) {
58
53
int [] newIdx = partition (arr , start , end );
59
- if (newIdx != null ) {
60
- quickSort (arr , start , newIdx [0 ]);
61
- quickSort (arr , newIdx [1 ], end );
54
+ if (isGoodPivot (newIdx [0 ], newIdx [1 ], start , end )) {
55
+ if (newIdx != null ) {
56
+ quickSort (arr , start , newIdx [0 ]);
57
+ quickSort (arr , newIdx [1 ], end );
58
+ }
59
+ } else {
60
+ quickSort (arr , start , end );
62
61
}
63
62
}
64
63
}
@@ -144,4 +143,43 @@ private static int random(int start, int end) {
144
143
return (int ) (Math .random () * (end - start + 1 )) + start ;
145
144
}
146
145
146
+ /**
147
+ * Checks if the pivot is a good pivot for the QuickSort algorithm.
148
+ * A good pivot helps avoid worst-case behavior in QuickSort.
149
+ *
150
+ * Since we have three-way partitioning, we cannot use 1/10, 9/10 split of the array as our good pivot condition.
151
+ * Note that our goal here is to ensure the sizes of the sub-arrays QuickSort is to recurse on are roughly the same
152
+ * to ensure that our partitioning is not too imbalanced. The pivot condition we chose is: the larger sub-array can
153
+ * be at most 9 times the size of the smaller sub-array.
154
+ *
155
+ * If n < 10, such a pivot condition would be meaningless, therefore always return true. This would cause
156
+ * the worst case recurrence relation to be T(n) = T(n-1) + O(n) => O(n^2) for small sub-arrays, but the overall
157
+ * asymptotic time complexity of Paranoid QuickSort is still O(nlogn).
158
+ *
159
+ * For an all-duplicates array, all pivots will be considered good pivots, therefore return true.
160
+ *
161
+ * @param firstPIdx The ending index of the < portion of the sub-array.
162
+ * @param secondPIdx The starting index of the > portion of the sub-array.
163
+ * @param start The starting index of the current sub-array.
164
+ * @param end The ending index of the current sub-array.
165
+ * @return True if the given index is a good pivot, false otherwise.
166
+ */
167
+ public static boolean isGoodPivot (int firstPIdx , int secondPIdx , int start , int end ) {
168
+ int n = end - start + 1 ;
169
+ if (firstPIdx >= start || secondPIdx <= end ) {
170
+ if (end - secondPIdx + 1 > 0 ) { // avoid division by zero
171
+ double ratio = (double ) (firstPIdx - start + 1 ) / (end - secondPIdx + 1 );
172
+ if (n >= 10 ) {
173
+ return ratio >= 1.0 / 9.0 && ratio <= 9 ;
174
+ } else {
175
+ return true ;
176
+ }
177
+ } else { // ratio is infinite, imbalanced partition => bad pivot
178
+ return false ;
179
+ }
180
+ } else { // all duplicates array
181
+ return true ;
182
+ }
183
+ }
184
+
147
185
}
0 commit comments