Java 中的基数排序-Radix Sort

1. 引言

在本教程中,我们将了解 Radix Sort,分析其性能,并了解其实现。

在这里,我们重点介绍使用 Radix Sort 对整数进行排序,但它不仅限于数字。我们也可以使用它来对其他类型进行排序,例如 String。

为了简单起见,我们将重点介绍数字以基数(基数)10 表示的十进制系统。

2. 算法概述

基数排序是一种排序算法,它根据数字的位置对数字进行排序。基本上,它使用数字中数字的位值。与大多数其他排序算法(例如合并排序、插入排序、冒泡排序)不同,它不会比较数字。

基数排序使用稳定的排序算法作为子程序对数字进行排序。我们在这里使用了计数排序的变体作为子例程,它使用基数对每个位置的数字进行排序。计数排序是一种稳定的排序算法,在实践中效果很好。

基数排序的工作原理是将数字从最低有效数字 (LSD) 排序到最高有效数字 (MSD)。我们还可以实现基数排序来处理来自 MSD 的数字。

3. 一个简单的例子

让我们通过一个示例看看它是如何工作的。让我们考虑以下数组:
在这里插入图片描述

3.1. 迭代 1

我们将通过处理来自 LSD 的数字并移向 MSD 来对这个数组进行排序。
因此,让我们从一个位置的数字开始:
在这里插入图片描述
在第一次迭代之后,数组现在如下所示:
在这里插入图片描述
请注意,数字已根据某个位置的数字进行排序。

3.2. 迭代 2

让我们继续看十位数字:
在这里插入图片描述
现在数组如下所示:
在这里插入图片描述
我们看到数字 7 占据了数组中的第一个位置,因为它在十位没有任何数字。我们也可以将其视为在十位有一个 0。

3.3. 迭代 3

让我们继续讨论百位位置的数字:
在这里插入图片描述
完成此迭代后,数组如下所示:
在这里插入图片描述
算法到此为止,所有元素都已排序。

4. 实施

现在让我们看一下实现。

void sort(int[] numbers) {
    int maximumNumber = findMaximumNumberIn(numbers);
    int numberOfDigits = calculateNumberOfDigitsIn(maximumNumber);
    int placeValue = 1;
    while (numberOfDigits-- > 0) {
        applyCountingSortOn(numbers, placeValue);
        placeValue *= 10;
    }
}

该算法的工作原理是找出数组中的最大数字,然后计算其长度。此步骤有助于我们确保对每个位值执行子例程。

例如,在数组 [7, 37, 68, 123, 134, 221, 387, 468, 769] 中,最大数字为 769,长度为 3。

因此,我们在每个位置的数字上迭代并应用子程序三次:

void applyCountingSortOn(int[] numbers, int placeValue) {

    int range = 10 // decimal system, numbers from 0-9

    // ...

    // calculate the frequency of digits
    for (int i = 0; i < length; i++) {
        int digit = (numbers[i] / placeValue) % range;
        frequency[digit]++;
    }

    for (int i = 1; i < range; i++) {
        frequency[i] += frequency[i - 1];
    }

    for (int i = length - 1; i >= 0; i--) {
        int digit = (numbers[i] / placeValue) % range;
        sortedValues[frequency[digit] - 1] = numbers[i];
        frequency[digit]--;
    }

    System.arraycopy(result, 0, numbers, 0, length); 

}

在子例程中,我们使用基数(范围)来计算每个数字的出现次数并增加其频率。因此,从 0 到 9 范围内的每个 bin 都会根据数字的频率获得一些值。然后,我们使用频率来定位数组中的每个元素。这也有助于我们最小化对数组进行排序所需的空间。

现在让我们测试一下我们的方法:

@Test
void givenUnsortedArray_whenRadixSort_thenArraySorted() {
    int[] numbers = {387, 468, 134, 123, 68, 221, 769, 37, 7};
    RadixSort.sort(numbers);
    int[] numbersSorted = {7, 37, 68, 123, 134, 221, 387, 468, 769};
    assertArrayEquals(numbersSorted, numbers); 
}

5. 基数排序与计数排序

在子程序中,频率阵列的长度为 10 (0-9)。在计数排序的情况下,我们不使用范围。频率数组的长度将是数组中的最大数字 + 1。因此,我们不会将它们划分为箱,而 Radix Sort 使用箱进行排序。

当数组的长度不比数组中的最大值小多少时,计数排序非常有效,而基数排序允许数组中的较大值。

6. 复杂性

Radix Sort 的性能取决于选择对数字进行排序的稳定排序算法。

在这里,我们使用基数排序对以 b 为基数的 n 个数字数组进行排序。在我们的例子中,基数是 10。我们应用了计数排序 d 次,其中 d 代表位数。因此,基数排序的时间复杂度变为 O(d * (n + b))。

空间复杂度为 O(n + b),因为我们在这里使用了 Counting Sort 的变体作为子程序。

7. 结论

在本文中,我们描述了 Radix 排序算法,并说明了如何实现它。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值