Skip to content

Commit 2753a1f

Browse files
authored
Merge pull request #46 from yeoshuheng/chkstyle
Radix Sort
2 parents ab57cf9 + 587a40c commit 2753a1f

File tree

3 files changed

+167
-43
lines changed

3 files changed

+167
-43
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Radix Sort
2+
3+
## Background
4+
5+
Radix Sort is a non-comparison based, stable sorting algorithm with a counting sort subroutine.
6+
7+
Radix Sort continuously sorts based on the least-significant segment of a element
8+
to the most-significant value of a element.
9+
10+
The definition of a 'segment' is user defined and defers from implementation to implementation.
11+
Within our implementation, we define each segment as a bit chunk.
12+
13+
For example, if we aim to sort integers, we can sort each element
14+
from the least to most significant digit, with the digits being our 'segments'.
15+
16+
Within our implementation, we take the binary representation of the elements and
17+
partition it into 8-bit segments, a integer is represented in 32 bits,
18+
this gives us 4 total segments to sort through.
19+
20+
Note that the binary representation is weighted positional,
21+
where each bit's value is dependent on its overall position
22+
within the representation (the n-th bit from the right represents *2^n*),
23+
hence we can actually increase / decrease the number segments we wish to conduct a split from.
24+
25+
![Radix Sort](https://miro.medium.com/v2/resize:fit:661/1*xFnpQ4UNK0TvyxiL8r1svg.png)
26+
27+
We place each element into a queue based on the number of possible segments that could be generated.
28+
Suppose the values of our segments are in base-10, (limited to a value within range *[0, 9]*),
29+
we get 10 queues. We can also see that radix sort is stable since
30+
they are enqueued in a manner where the first observed element remains at the head of the queue
31+
32+
*Source: Level Up Coding*
33+
34+
### Implementation Invariant
35+
At the start of the *i-th* segment we are sorting on, the array has already been sorted on the
36+
previous *(i - 1)-th* segments.
37+
38+
### Common Misconceptions
39+
40+
While Radix Sort is non-comparison based,
41+
the that total ordering of elements is still required.
42+
This total ordering is needed because once we assigned a element to a order based on a segment,
43+
the order *cannot* change unless deemed by a segment with a higher significance.
44+
Hence, a stable sort is required to maintain the order as
45+
the sorting is done with respect to each of the segments.
46+
47+
## Complexity Analysis
48+
**Time**:
49+
Let *b* be the length of a single element we are sorting and *r* is the amount of bit-string
50+
we plan to break each element into.
51+
(Essentially, *b/r* represents the number of segments we
52+
sort on and hence the number of passes we do during our sort).
53+
54+
Note that we derive *(2^r + n)* from the counting sort subroutine,
55+
since we have *2^r* represents the range since we have *r* bits.
56+
57+
We get a general time complexity of *O((b/r) * (2^r + n))*
58+
59+
**Space**: *O(n + 2^r)*
60+
61+
## Notes
62+
- Radix sort's time complexity is dependent on the maximum number of digits in each element,
63+
hence it is ideal to use it on integers with a large range and with little digits.
64+
- This could mean that Radix Sort might end up performing worst on small sets of data
65+
if any one given element has a in-proportionate amount of digits.
Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,73 @@
11
package algorithms.sorting.radixSort;
22

33
/**
4-
* Implementation of Radix sort.
5-
* O((b/r)*(N + 2^r))
6-
* where N is the number of integers,
7-
* b is the total number of bits (32 bits for int),
8-
* and r is the number of bits for each segment.
9-
* Space: O(N) auxiliary space.
10-
*/
4+
* This class implements a Radix Sort Algorithm.
5+
*/
116
public class RadixSort {
12-
private static final int NUM_BITS = 8;
13-
private static final int NUM_SEGMENTS = 4;
147

15-
private static int getSegmentMasked(int num, int segment) {
16-
int mask = ((1 << NUM_BITS) - 1) << (segment * NUM_BITS);
17-
return (num & mask) >> (segment * NUM_BITS);
18-
}
8+
private static final int NUM_BITS = 8;
9+
private static final int NUM_SEGMENTS = 4;
1910

20-
private static void radixSort(int[] arr, int[] sorted) {
21-
// sort the N numbers by segments, starting from left-most segment
22-
for (int i = 0; i < NUM_SEGMENTS; i++) {
23-
int[] freqMap = new int[1 << NUM_BITS]; // at most this number of elements
11+
/**
12+
* Creates masking on the segment to obtain the value of the digit.
13+
*
14+
* @param num The number.
15+
* @param segment The segment we are interested in.
16+
*
17+
* @return The value of the digit in the number at the given segment.
18+
*/
19+
private static int getSegmentMasked(int num, int segment) {
20+
// Bit masking here to extract each segment from the integer.
21+
int mask = ((1 << NUM_BITS) - 1) << (segment * NUM_BITS);
22+
return (num & mask) >> (segment * NUM_BITS);
23+
}
2424

25-
// count each element
26-
for (int num : arr) {
27-
freqMap[getSegmentMasked(num, i)]++;
28-
}
29-
// get prefix sum
30-
for (int j = 1; j < freqMap.length; j++) {
31-
freqMap[j] += freqMap[j-1];
32-
}
33-
// place each number in its correct sorted position up until the given segment
34-
for (int k = arr.length-1; k >= 0; k--) {
35-
int curr = arr[k];
36-
int id = getSegmentMasked(curr, i);
37-
sorted[freqMap[id] - 1] = curr;
38-
freqMap[id]--;
39-
}
40-
// we do a swap so that our results above for this segment is saved and passed as input to the next segment
41-
int[] tmp = arr;
42-
arr = sorted;
43-
sorted = tmp;
44-
}
45-
sorted = arr;
46-
}
25+
/**
26+
* Radix sorts a given input array and updates the output array in-place.
27+
*
28+
* @param arr original input array.
29+
* @param sorted output array.
30+
*/
31+
private static void radixSort(int[] arr, int[] sorted) {
32+
// sort the N numbers by segments, starting from left-most segment
33+
for (int i = 0; i < NUM_SEGMENTS; i++) {
34+
int[] freqMap = new int[1 << NUM_BITS]; // at most this number of elements
4735

48-
public static void radixSort(int[] arr) {
49-
int[] sorted = new int[arr.length];
50-
radixSort(arr, sorted);
51-
arr = sorted; // swap back lol
36+
// count each element
37+
for (int num : arr) {
38+
freqMap[getSegmentMasked(num, i)]++;
39+
}
40+
// get prefix sum
41+
for (int j = 1; j < freqMap.length; j++) {
42+
freqMap[j] += freqMap[j - 1];
43+
}
44+
// place each number in its correct sorted position up until the given segment
45+
for (int k = arr.length - 1; k >= 0; k--) {
46+
int curr = arr[k];
47+
int id = getSegmentMasked(curr, i);
48+
sorted[freqMap[id] - 1] = curr;
49+
freqMap[id]--;
50+
}
51+
// We do a swap so that our results above for this segment is
52+
// saved and passed as input to the next segment.
53+
// By doing this we no longer need to create a new array
54+
// every time we shift to a new segment to sort.
55+
// We can constantly reuse the array, allowing us to only use O(n) space.
56+
int[] tmp = arr;
57+
arr = sorted;
58+
sorted = tmp;
5259
}
60+
sorted = arr;
61+
}
62+
63+
/**
64+
* Calls radix sort inplace on a given array.
65+
*
66+
* @param arr The array to be sorted.
67+
*/
68+
public static void radixSort(int[] arr) {
69+
int[] sorted = new int[arr.length];
70+
radixSort(arr, sorted);
71+
arr = sorted; // swap back lol
72+
}
5373
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package algorithms.sorting.radixSort;
2+
3+
import static org.junit.Assert.assertArrayEquals;
4+
5+
import java.util.Arrays;
6+
import org.junit.Test;
7+
8+
public class RadixSortTest {
9+
@Test
10+
public void test_radixSort_shouldReturnSortedArray() {
11+
int[] firstArray = new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2,
12+
15, 12, 20, 21, 120, 11, 5, 7, 85, 30};
13+
int[] firstResult = Arrays.copyOf(firstArray, firstArray.length);
14+
RadixSort.radixSort(firstResult);
15+
16+
int[] secondArray = new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4,
17+
3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
18+
int[] secondResult = Arrays.copyOf(secondArray, secondArray.length);
19+
RadixSort.radixSort(secondResult);
20+
21+
int[] thirdArray = new int[] {};
22+
int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length);
23+
RadixSort.radixSort(thirdResult);
24+
25+
int[] fourthArray = new int[] {1};
26+
int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length);
27+
RadixSort.radixSort(fourthResult);
28+
29+
Arrays.sort(firstArray);
30+
Arrays.sort(secondArray);
31+
Arrays.sort(thirdArray);
32+
Arrays.sort(fourthArray);
33+
34+
assertArrayEquals(firstResult, firstArray);
35+
assertArrayEquals(secondResult, secondArray);
36+
assertArrayEquals(thirdResult, thirdArray);
37+
assertArrayEquals(fourthResult, fourthArray);
38+
}
39+
}

0 commit comments

Comments
 (0)