要点:利用空间换时间,有桶排序的思想,按照基数规则转换,使空间开销较小,但理解起来比计数排序复杂的多。
1 import java.util.Random; 2 3 public class RadixSort { 4 5 public void sort(int arr[]) { 6 int max = arr[0]; 7 for (int i = 1; i < arr.length; i++) { 8 if (arr[i] > max) { 9 max = arr[i]; 10 } 11 } 12 // 计数 13 int[] bucketArr; 14 // 输出 15 int[] outputArr = new int[arr.length]; 16 // 根据最大值求指数 17 int exp = (int) Math.pow(10, String.valueOf(max).length() - 1); 18 for (int i = 1; i <= exp; i *= 10) { 19 bucketArr = new int[10]; 20 for (int j = 0; j < arr.length; j++) { 21 int index = arr[j] / i % 10; 22 bucketArr[index]++; 23 } 24 printArr(bucketArr, " => 计数bucket"); 25 // 转换 => 见转换说明 26 for (int j = 1; j < 10; j++) { 27 bucketArr[j] += bucketArr[j - 1]; 28 } 29 printArr(bucketArr, " => 转换bucket"); 30 // 根据转换后的bucket数组,将原数组的值映射到output数组 => 见映射说明 31 // 注意采用的是倒序遍历 => 见倒序说明 32 for (int j = arr.length - 1; j >= 0; j--) { 33 int index = arr[j] / i % 10; 34 int outputIndex = bucketArr[index]--; 35 outputArr[outputIndex - 1] = arr[j]; 36 } 37 printArr(outputArr," => output数组"); 38 for (int j = 0; j < outputArr.length; j++) { 39 arr[j] = outputArr[j]; 40 } 41 } 42 } 43 44 private void printArr(int[] arr, String message) { 45 for (int n : arr) { 46 System.out.print(n + ","); 47 } 48 System.out.println(message); 49 } 50 51 public static void main(String[] args) { 52 int capacity = 10; 53 int[] arr = new int[capacity]; 54 for (int i = 0; i < capacity; i++) { 55 arr[i] = new Random().nextInt(500); 56 } 57 RadixSort rs = new RadixSort(); 58 rs.printArr(arr, " => 原数组"); 59 rs.sort(arr); 60 rs.printArr(arr, " => 排序后数组"); 61 } 62 63 /** 64 * 转换说明: 65 * 0,1,0,0,2,4,0,2,1,0, => 计数bucket 66 * 转换公式:bucekt[i] += bucket[i - 1] 67 * 0,1,1,1,3,7,7,9,10,10, => 转换bucket 68 * 说白了就是为了统计到[i]位置,bucket获取的元素数 69 * 70 * 映射说明: 71 * 45,5,21,245,454,204,138,137,77,445, => 原数组 72 * 0,1,1,1,3,7,7,9,10,10, => 转换bucket 73 * 当前的exp是1: 74 * 首先计算原数组中的值在bucket哪个位置:=> index = 445 / 1 % 10 = 5 75 * 找到bucket[5]对应的值是7,7代表的是个数,因此要减去1才代表下标。 76 * out[6] = 445 77 * 21,454,204,45,5,245,445,137,77,138, => output数组 78 * 79 * 倒序说明: 80 * 204,5,21,137,138,45,245,445,454,77, => 按照十位已经排好序了 81 * 此时,数组十位是有序状态,【从小到大】。 82 * 4,2,2,0,2,0,0,0,0,0, => 计数bucket => 其中5,21,45,77在0坐标处计数,为4 83 * 4,6,8,8,10,10,10,10,10,10, => 转换bucket 84 * 求百位时,所有的个位和十位在求对应的bucket位置时都是0。 85 * 如果按正序遍历,就相当于按照从大到小的顺序填入output数组。 86 * 如正序遍历,拿到5,求得bucket坐标是0,读取值4,坐标为3,output[3]=5,读取值-1 87 * 继续遍历到21,求得bucket坐标是0,读取值3,坐标为2,output[2]=21,读取值-1 88 * 顺序反了,因此需要倒序遍历。 89 * 90 * 利用空间换时间,但是利用了转换规则,使空间开销较小 91 * => 遍历了exp * (4m + 10) 次 92 * => 时间复杂度O(n) => exp还是有限的 93 * => 稳定性:稳定 => 都是整数 94 * 95 */ 96 97 }