function radixSort(arr) {if (!Array.isArray(arr) || arr.length <= 1) {return arr;}// 1. 找到数组中的最大值,以确定最大位数let max = Math.max(...arr);let exp = 1; // 1, 10, 100...// 2. 循环执行计数排序,从个位到最高位while (max / exp >= 1) {arr = countingSort(arr, exp);exp *= 10;}return arr;
}function countingSort(arr, exp) {let n = arr.length;let output = new Array(n);let count = new Array(10).fill(0); // 0-9 十个桶// 3. 统计每个桶中元素的数量for (let i = 0; i < n; i++) {let digit = Math.floor(arr[i] / exp) % 10;count[digit]++;}// 4. 计算每个桶的结束索引for (let i = 1; i < 10; i++) {count[i] += count[i - 1];}// 5. 将元素放入输出数组中for (let i = n - 1; i >= 0; i--) {let digit = Math.floor(arr[i] / exp) % 10;output[count[digit] - 1] = arr[i];count[digit]--;}// 6. 将排序后的数组复制回原数组return output;
}// 示例用法:
let arr = [170, 45, 75, 90, 802, 24, 2, 66];
let sortedArr = radixSort(arr);
console.log(sortedArr); // [2, 24, 45, 66, 75, 90, 170, 802]
时间复杂度:
基数排序的时间复杂度为 O(nk),其中 n 是数组的长度,k 是最大数的位数。在计数排序部分,我们遍历数组两次,所以是 O(n)。由于我们需要对每一位都执行一次计数排序,所以总的时间复杂度是 O(nk)。
- 如果 k 相对于 n 来说很小(例如,k 是常数或 log n),那么基数排序的时间复杂度可以近似为 O(n),使其成为一种线性时间排序算法。
- 但是,如果 k 很大(例如,k 比 n 大得多),那么基数排序的性能可能会下降。
空间复杂度:
基数排序的空间复杂度为 O(n+k)。计数排序需要一个大小为 k 的计数数组和一个大小为 n 的输出数组。因此,总的空间复杂度为 O(n+k)。由于 k 通常比 n 小得多,所以空间复杂度可以近似为 O(n)。
前端开发中的应用场景:
虽然基数排序在前端开发中不如快速排序、归并排序或内置的 Array.prototype.sort()
常用,但在特定情况下,它仍然可以是一个不错的选择:
- 排序非负整数: 基数排序最适合排序非负整数。如果你的数据是由非负整数组成,并且最大值的位数相对较小,那么基数排序可能比其他排序算法更高效。
- 排序字符串: 可以将字符串转换为数字(例如,ASCII 码)后使用基数排序。
- 自定义排序规则: 可以根据特定需求修改基数排序,例如按字符串的长度或其他自定义规则进行排序。
与其他排序算法的比较:
- 快速排序/归并排序: 通常情况下,快速排序和归并排序是更通用的选择,平均时间复杂度为 O(n log n)。
- 计数排序: 计数排序适用于排序范围较小的整数,时间复杂度为 O(n+k),其中 k 是排序范围。基数排序可以看作是多次应用计数排序。
总而言之,基数排序是一种高效的排序算法,尤其适用于排序非负整数。在特定情况下,它可能比其他排序算法更高效。 理解其时间和空间复杂度可以帮助你在前端开发中做出明智的算法选择。