Here's a perhaps naive but simple implementation of Radix sort in Java. It works with any radix, and both positive and negative numbers.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class RadixSortWithDivision {
public static final int DEFAULT_RADIX = 10;
public static void sort(int[] arr) {
sort(arr, DEFAULT_RADIX);
}
public static void sort(int[] arr, int radix) {
int maxDigits = 1 + (int) (Math.log(max(arr)) / Math.log(radix));
int divisor = 1;
for (int pos = 0; pos < maxDigits; ++pos) {
List<List<Integer>> buckets = splitToBuckets(arr, divisor, radix);
flattenBuckets(arr, buckets);
divisor *= radix;
}
List<List<Integer>> buckets = splitBySign(arr);
flattenBuckets(arr, buckets);
}
private static List<List<Integer>> splitToBuckets(int[] arr, int divisor, int radix) {
List<List<Integer>> buckets = new ArrayList<>();
for (int i = 0; i < radix; ++i) {
buckets.add(new LinkedList<>());
}
for (int num : arr) {
int bucketIndex = Math.abs(num) / divisor % radix;
buckets.get(bucketIndex).add(num);
}
return buckets;
}
private static List<List<Integer>> splitBySign(int[] arr) {
List<Integer> positive = new LinkedList<>();
List<Integer> negative = new LinkedList<>();
for (int num : arr) {
if (num >= 0) {
positive.add(num);
} else {
negative.add(0, num);
}
}
return Arrays.asList(negative, positive);
}
private static void flattenBuckets(int[] arr, List<? extends List<Integer>> buckets) {
int i = 0;
for (List<Integer> bucket : buckets) {
for (int num : bucket) {
arr[i++] = num;
}
}
}
private static int max(int[] arr) {
int max = Integer.MIN_VALUE;
for (int num : arr) {
if (num > max) {
max = num;
}
}
return max;
}
}
Unit tests:
import org.junit.Test;
import java.util.Arrays;
import java.util.Random;
import static org.junit.Assert.assertArrayEquals;
public abstract class SortTest {
abstract void sort(int[] arr);
private int[] newRandomArray(int num) {
Random random = new Random(0);
int[] arr = new int[num];
for (int i = 0; i < arr.length; ++i) {
arr[i] = random.nextInt();
}
return arr;
}
private void sortAndVerify(int[] arr) {
int[] copy = arr.clone();
Arrays.sort(copy);
sort(arr);
assertArrayEquals(copy, arr);
}
@Test
public void test_empty() {
sortAndVerify(new int[0]);
}
@Test
public void test_1() {
sortAndVerify(new int[]{1});
}
@Test
public void test_1_2() {
sortAndVerify(new int[]{1, 2});
}
@Test
public void test_2_1() {
sortAndVerify(new int[]{2, 1});
}
@Test
public void test_1_2_3() {
sortAndVerify(new int[]{1, 2, 3});
}
@Test
public void test_3_2_1() {
sortAndVerify(new int[]{3, 2, 1});
}
@Test
public void test_random_10() {
sortAndVerify(newRandomArray(10));
}
@Test
public void test_random_1000() {
sortAndVerify(newRandomArray(1000));
}
}
public class RadixSortWithDivisionTest extends SortTest {
@Override
void sort(int[] arr) {
RadixSortWithDivision.sort(arr);
}
}
How would you improve this?
How would you improve the unit test cases?