Skip to content

Commit 40489bd

Browse files
committed
Added Hoare's QuickSort
1 parent fd698c8 commit 40489bd

File tree

7 files changed

+177
-2
lines changed

7 files changed

+177
-2
lines changed

assets/Hoares.jpeg

56.7 KB
Loading
File renamed without changes.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package src.algorithms.sorting.quickSort.hoares;
2+
3+
import java.lang.Math;
4+
5+
/**
6+
* Here, we are implementing Hoares's QuickSort where we sort the array in increasing (or more precisely,
7+
* non-decreasing) order.
8+
*
9+
* Brief Description:
10+
* Hoare's QuickSort operates by selecting a pivot element from the input array and rearranging the elements such that
11+
* all elements in A[start, returnIdx] are <= pivot and all elements in A[returnIdx + 1, end] are >= pivot, where
12+
* returnIdx is the index returned by the sub-routine partition.
13+
*
14+
* After partitioning, the algorithm recursively applies the same process to the left and right sub-arrays, effectively
15+
* sorting the entire array.
16+
*
17+
* The Hoare's partition scheme works by initializing two pointers that start at two ends. The two pointers move toward
18+
* each other until an inversion is found. This inversion happens when the left pointer is at an element >= pivot, and
19+
* the right pointer is at an element <= pivot. When an inversion is found, the two values are swapped and the pointers
20+
* continue moving towards each other.
21+
*
22+
* Implementation Invariant:
23+
* All elements in A[start, returnIdx] are <= pivot and all elements in A[returnIdx + 1, end] are >= pivot.
24+
*
25+
* Note:
26+
* - Hoare's partition scheme does not necessarily put the pivot in its correct position. It merely partitions the
27+
* array into <= pivot and >= pivot portions.
28+
* - This is in contrast to Lomuto's partition scheme. Hoare's uses two pointers, while Lomuto's uses one. Hoare's
29+
* partition scheme is generally more efficient as it requires less swaps. See more at
30+
* https://www.geeksforgeeks.org/hoares-vs-lomuto-partition-scheme-quicksort/.
31+
*/
32+
33+
public class QuickSort {
34+
/**
35+
* Sorts the given array in-place in non-decreasing order.
36+
* @param arr array to be sorted.
37+
*/
38+
public static void sort(int[] arr) {
39+
int n = arr.length;
40+
quickSort(arr, 0, n - 1);
41+
}
42+
43+
/**
44+
* Recursively sorts the sub-array from index 'start' to index 'end' in non-decreasing order
45+
* using the QuickSort algorithm.
46+
*
47+
* @param arr the array containing the sub-array to be sorted.
48+
* @param start the starting index (inclusive) of the sub-array to be sorted.
49+
* @param end the ending index (inclusive) of the sub-array to be sorted.
50+
*/
51+
public static void quickSort(int[] arr, int start, int end) {
52+
if (start < end) {
53+
int pIdx = partition(arr, start, end);
54+
quickSort(arr, start, pIdx);
55+
quickSort(arr, pIdx + 1, end);
56+
}
57+
}
58+
59+
/**
60+
* Partitions the sub-array from index 'start' to index 'end' around a randomly selected pivot element.
61+
* After this sub-routine is complete, all elements in A[start, returnIdx] are <= pivot and all elements in
62+
* A[returnIdx + 1, end] are >= pivot.
63+
*
64+
* Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we need to iterate
65+
* through every element in the sub-array once.
66+
*
67+
* @param arr the array containing the sub-array to be partitioned.
68+
* @param start the starting index (inclusive) of the sub-array to be partitioned.
69+
* @param end the ending index (inclusive) of the sub-array to be partitioned.
70+
* @return the index at which the array is partitioned at
71+
*/
72+
private static int partition(int[] arr, int start, int end) {
73+
int pIdx = random(start, end);
74+
int pivot = arr[pIdx];
75+
int i = start - 1;
76+
int j = end + 1;
77+
78+
while (true) {
79+
do {
80+
i++;
81+
} while (arr[i] < pivot);
82+
83+
do {
84+
j--;
85+
} while (arr[j] > pivot);
86+
87+
if (i >= j) {
88+
return j;
89+
}
90+
swap(arr, i, j);
91+
}
92+
93+
}
94+
95+
/**
96+
* Swaps the elements at indices 'i' and 'j' in the given array.
97+
*
98+
* @param arr the array in which the elements should be swapped.
99+
* @param i the index of the first element to be swapped.
100+
* @param j the index of the second element to be swapped.
101+
*/
102+
private static void swap(int[] arr, int i, int j) {
103+
int temp = arr[i];
104+
arr[i] = arr[j];
105+
arr[j] = temp;
106+
}
107+
108+
/**
109+
* Generates a random integer within the range [start, end].
110+
*
111+
* @param start the lower bound of the random integer (inclusive).
112+
* @param end the upper bound of the random integer (inclusive).
113+
* @return a random integer within the specified range.
114+
*/
115+
private static int random(int start, int end) {
116+
return (int) (Math.random() * (end - start + 1)) + start;
117+
}
118+
119+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
![Hoare's QuickSort with random pivot](../../../../../assets/Hoares.jpeg)

src/algorithms/sorting/quickSort/lomuto/QuickSort.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
* Time:
2525
* - Expected worst case (poor choice of pivot): O(n^2)
2626
* - Expected average case: O(nlogn)
27-
* - Expected Best case (balanced pivot): O(nlogn)
27+
* - Expected best case (balanced pivot): O(nlogn)
2828
*
2929
* In the best case of a balanced pivot, the partitioning process divides the array in half, which leads to log n
3030
* levels of recursion. Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we

src/algorithms/sorting/quickSort/lomuto/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ If we use randomised pivot selection, the idea is very similar to the above impl
88
need to do is to swap the random pivot to the first element in the array, then partition as per usual,
99
then swap the pivot back to its correct position. Below is an illustration:
1010

11-
![Lomuto's QuickSort with random pivot](../../../../../assets/QuickSortRandomPivot.jpeg)
11+
![Lomuto's QuickSort with random pivot](../../../../../assets/Lomutos.jpeg)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package test.algorithms.quickSort.hoares;
2+
3+
import org.junit.Test;
4+
5+
import src.algorithms.sorting.quickSort.hoares.QuickSort;
6+
7+
import java.util.Arrays;
8+
9+
import static org.junit.Assert.assertArrayEquals;
10+
11+
public class QuickSortTest {
12+
13+
@Test
14+
public void test_selectionSort_shouldReturnSortedArray() {
15+
int[] firstArray =
16+
new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 85, 30};
17+
int[] firstResult = Arrays.copyOf(firstArray, firstArray.length);
18+
QuickSort.sort(firstResult);
19+
20+
int[] secondArray
21+
= new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
22+
int[] secondResult = Arrays.copyOf(secondArray, secondArray.length);
23+
QuickSort.sort(secondResult);
24+
25+
int[] thirdArray = new int[] {};
26+
int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length);
27+
QuickSort.sort(thirdResult);
28+
29+
int[] fourthArray = new int[] {1};
30+
int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length);
31+
QuickSort.sort(fourthResult);
32+
33+
int[] fifthArray = new int[] {5,1,1,2,0,0};
34+
int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length);
35+
QuickSort.sort(fifthResult);
36+
37+
int[] sixthArray = new int[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1};
38+
int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length);
39+
QuickSort.sort(sixthResult);
40+
41+
Arrays.sort(firstArray); // get expected result
42+
Arrays.sort(secondArray); // get expected result
43+
Arrays.sort(thirdArray); // get expected result
44+
Arrays.sort(fourthArray); // get expected result
45+
Arrays.sort(fifthArray); // get expected result
46+
Arrays.sort(sixthArray); // get expected result
47+
48+
assertArrayEquals(firstResult, firstArray);
49+
assertArrayEquals(secondResult, secondArray);
50+
assertArrayEquals(thirdResult, thirdArray);
51+
assertArrayEquals(fourthResult, fourthArray);
52+
assertArrayEquals(fifthResult, fifthArray);
53+
assertArrayEquals(sixthResult, sixthArray);
54+
}
55+
}

0 commit comments

Comments
 (0)