基数排序详解:LSD和MSD的实现与比较

排序算法是计算机科学中基础而又重要的部分,其中基数排序(Radix Sort)是一种高效的非比较排序算法。基数排序有两种主要的实现方式:LSD(Least Significant Digit)基数排序和MSD(Most Significant Digit)基数排序。本文将详细介绍这两种排序算法,并提供Java实现代码。

什么是基数排序?

基数排序是一种基于“位”的排序算法,通过将数据按位进行多次排序来实现最终的排序效果。它特别适用于整数和字符串的排序。基数排序有两种主要的变种:LSD和MSD,它们的主要区别在于排序的方向。

LSD基数排序

LSD基数排序从数据的最低有效位(最右边的位)开始排序,一直排序到最高有效位。每一轮排序都使用稳定的计数排序来确保排序的正确性。

LSD基数排序的步骤
  1. 确定最大数的位数:找出数组中最大数的位数 (d)。
  2. 从最低有效位开始排序:对每一位进行排序,使用计数排序(或桶排序)来保证排序的稳定性。

MSD基数排序

MSD基数排序从数据的最高有效位(最左边的位)开始排序,逐位向最低有效位排序。每一轮排序都将数据划分为多个子数组,并递归地对这些子数组进行排序。

MSD基数排序的步骤
  1. 确定最大数的位数:找出数组中最大数的位数 (d)。
  2. 从最高有效位开始排序
    • 对当前位进行排序,使用计数排序或桶排序来保证排序的稳定性。
    • 对每个子数组递归地进行排序,处理下一位。

抽象计数排序

为了提高代码的复用性,我们将计数排序抽象出来,并在LSD和MSD基数排序中调用。

抽象计数排序的Java实现
import java.util.Arrays;

public class CountingSort {

    public static void countingSort(int[] arr, int exp) {
        int n = arr.length;
        int[] output = new int[n];
        int[] count = new int[10];
        Arrays.fill(count, 0);

        // 统计每个桶中的计数
        for (int i = 0; i < n; i++) {
            count[(arr[i] / exp) % 10]++;
        }

        // 计算累加计数
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }

        // 构建输出数组
        for (int i = n - 1; i >= 0; i--) {
            int index = (arr[i] / exp) % 10;
            output[count[index] - 1] = arr[i];
            count[index]--;
        }

        // 将排序后的数组复制回原数组
        System.arraycopy(output, 0, arr, 0, n);
    }
}

LSD基数排序的实现

使用抽象出来的计数排序方法来实现LSD基数排序:

import java.util.Arrays;

public class LSDRadixSort {

    private static int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        return max;
    }

    public static void radixSort(int[] arr) {
        int max = getMax(arr);

        // 从个位开始,对每个位进行排序
        for (int exp = 1; max / exp > 0; exp *= 10) {
            CountingSort.countingSort(arr, exp);
        }
    }

    public static void main(String[] args) {
        int[] arr = {170, 45, 75, 90, 802, 24, 2, 66};
        System.out.println("Original array: " + Arrays.toString(arr));
        radixSort(arr);
        System.out.println("Sorted array: " + Arrays.toString(arr));
    }
}

MSD基数排序的实现

使用抽象出来的计数排序方法来实现MSD基数排序:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MSDRadixSort {

    private static int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        return max;
    }

    private static void msdRadixSort(int[] arr, int exp) {
        if (arr.length <= 1) {
            return;
        }

        CountingSort.countingSort(arr, exp);

        // 创建桶以保存每个数字的分割结果
        List<List<Integer>> buckets = new ArrayList<>(10);
        for (int i = 0; i < 10; i++) {
            buckets.add(new ArrayList<>());
        }

        // 将元素分配到相应的桶中
        for (int num : arr) {
            buckets.get((num / exp) % 10).add(num);
        }

        // 对每个桶递归进行排序
        int index = 0;
        for (List<Integer> bucket : buckets) {
            int[] bucketArray = bucket.stream().mapToInt(Integer::intValue).toArray();
            if (exp > 1) {
                msdRadixSort(bucketArray, exp / 10);
            }
            for (int num : bucketArray) {
                arr[index++] = num;
            }
        }
    }

    public static void radixSort(int[] arr) {
        int max = getMax(arr);
        int maxDigit = (int) Math.pow(10, (int) Math.log10(max));
        msdRadixSort(arr, maxDigit);
    }

    public static void main(String[] args) {
        int[] arr = {170, 45, 75, 90, 802, 24, 2, 66};
        System.out.println("Original array: " + Arrays.toString(arr));
        radixSort(arr);
        System.out.println("Sorted array: " + Arrays.toString(arr));
    }
}

LSD和MSD基数排序的比较

相同点

  • 都是非比较排序算法。
  • 时间复杂度都是 (O(d \times (n + k))),其中 (d) 是最大元素的位数,(n) 是数组长度,(k) 是计数排序的范围(通常为10)。
  • 都使用计数排序来保证每轮排序的稳定性。

不同点

  • 排序方向
    • LSD基数排序从最低有效位开始排序。
    • MSD基数排序从最高有效位开始排序。
  • 实现复杂度
    • LSD基数排序实现较为简单。
    • MSD基数排序实现相对复杂,需要递归处理每个子数组。
  • 适用场景
    • LSD基数排序适用于整数和固定长度字符串。
    • MSD基数排序适用于整数和长字符串,特别是当数据范围较大时。

总结

通过将计数排序抽象出来,我们简化了LSD和MSD基数排序的实现。这样不仅提高了代码的复用性和可读性,也使得修改和扩展算法变得更加容易。无论是从最低有效位开始排序的LSD基数排序,还是从最高有效位开始排序的MSD基数排序,都可以利用同一个计数排序模块来实现其核心功能。通过本文的介绍和代码实现,相信读者已经对LSD和MSD基数排序有了更深入的了解。

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

poison_Program

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值