Radix Sort是一种有效的基于非比较的排序算法,可以按线性O(N)时间复杂度对数据集进行排序,因此比其他同类竞争算法要好快速排序。它使用另一种算法计数排序 作为子例程。
“基数排序”利用以下思想:
- 整数中的位数取决于:
- 它的基础
- 与数据相比要少得多
数字线性增加,但数字对数增加。
开发基数排序可以对大整数排序。它会将整数视为一串数字,因此我们也可以使用“基数排序”对字符串进行排序。
算法
- 对于i从最低有效数字到最高有效数字变化的每个数字i,都要执行以下操作。
- 使用以下命令对输入数组进行排序 计数排序(或任何稳定的排序)根据第i个数字。
例子
假设输入数组为:
[326、453、608、835、751、435、704、690]
基于该算法,我们将根据个位数(最低有效数字)对输入数组进行排序。
原始数组基于 [6、3、8、5、1、5、4、0] 使用计数排序
所以,数组变成 [690、751、453、704、835、435、326、608]
现在,我们将根据十位数进行排序:
上面部分排序的数组是基于 [9、5、5、0、3、3、2、0] 使用计数排序
现在,该数组变为: [704、608、326、835、435、751、453、690]
最后,我们根据百位数(最高有效位数)进行排序:
上面部分排序的数组是基于 [7、6、3、8、4、7、4、6] 使用计数排序
该数组变为: [326、435、453、608、690、704、751、835] 排序。
请按照以下图片了解概念:
为什么在Radix Sort中可以一次排序一位数字?
伪码
Radix-Sort(A, d)
for j = 1 to d do
int count[10] = {0};
for i = 0 to n do
count[key of(A[i]) in pass j]++
for k = 1 to 10 do
count[k] = count[k] + count[k-1]
for i = n-1 downto 0 do
result[ count[key of(A[i])] ] = A[j]
count[key of(A[i])]--
for i=0 to n do
A[i] = result[i]
end for(j)
end func
复杂度分析
最佳案例时间复杂度:Ω(nk)
平均案例时间复杂度:Θ(nk)
最坏案例时间复杂度:O(nk)
空间复杂度:O(n+k)
其中n是输入数据的数量,k是输入数据中的最大元素
设输入整数为d位。“基数排序”取O(d(n + b)) *时间,其中b是表示数字的基数,例如对于十进制,b是10。d的值是多少?如果k是最大可能值,则d将为O(logb(k))。因此,总体时间复杂度为O((n + b)* logb(k))。它看起来比基于比较的大k排序算法的时间复杂度更高。
让我们首先限制k。令k <= nc,其中c为常数。在这种情况下,复杂度变为O(nLogb(n))。但是它仍然不能胜过基于比较的排序算法。
如果我们增大b的值怎么办?使时间复杂度线性化的b的值应该是多少?如果将b设置为n,则时间复杂度为O(n)。换句话说,如果数字以基数n表示(或者每个数字取log2(n)位),则我们可以对范围从1到nc的整数数组进行排序。
Java实现
// Radix sort Java implementation
import java.util.*;
class RadixSortDemo2 {
// A utility function to get maximum value in arr[]
static int getMax(int arr[]) {
int n = arr.length;
int mx = arr[0];
for (int i = 1; i < n; i++)
if (arr[i] > mx)
mx = arr[i];
return mx;
}
// A function to do counting sort of arr[] according to
// the digit represented by exp.
static void countSort(int arr[], int exp) {
int n = arr.length;
int output[] = new int[n]; // output array
int i;
int count[] = new int[10];
Arrays.fill(count, 0);
// Store count of occurrences in count[]
for (i = 0; i < n; i++)
count[(arr[i] / exp) % 10]++;
// Change count[i] so that count[i] now contains
// actual position of this digit in output[]
for (i = 1; i < 10; i++)
count[i] += count[i - 1];
// Build the output array
for (i = n - 1; i >= 0; i--) {
output[count[(arr[i] / exp) % 10] - 1] = arr[i];
count[(arr[i] / exp) % 10]--;
}
// Copy the output array to arr[], so that arr[] now
// contains sorted numbers according to curent digit
for (i = 0; i < n; i++)
arr[i] = output[i];
}
// The main function to that sorts arr[] of size n using
// Radix Sort
static void radixSort(int arr[]) {
// Find the maximum number to know number of digits
int m = getMax(arr);
// Do counting sort for every digit. Note that instead
// of passing digit number, exp is passed. exp is 10^i
// where i is current digit number
for (int exp = 1; m / exp > 0; exp *= 10) {
countSort(arr, exp);
System.out.println(exp + "位处理:" + Arrays.toString(arr));
}
}
/* Driver function to check for above function */
public static void main(String[] args) {
int arr[] = { 326, 453, 608, 835, 751, 435, 704, 690 };
System.out.println("未排序:" + Arrays.toString(arr));
radixSort(arr);
System.out.println("排序后:" + Arrays.toString(arr));
}
}
输出:
未排序:[326, 453, 608, 835, 751, 435, 704, 690]
1位处理:[690, 751, 453, 704, 835, 435, 326, 608]
10位处理:[704, 608, 326, 835, 435, 751, 453, 690]
100位处理:[326, 435, 453, 608, 690, 704, 751, 835]
排序后:[326, 435, 453, 608, 690, 704, 751, 835]
好处
Radix Sort的优点是:
- 键短时(即数组元素的范围较小时)快。
- 用于后缀数组构造算法,例如Manber算法和DC3算法。
- 基数排序是稳定的排序,因为具有相等值的元素的相对顺序得以保持。
缺点
Radix Sort的缺点是:
- 由于“基数排序”取决于数字或字母,因此“基数排序”比其他类型的灵活性要差得多。因此,对于每种不同类型的数据,都需要重写它。
- 与其他排序算法相比,Radix排序的常数更大。
- 与就地排序的Quicksort相比,它占用了更多空间。
- 如果操作效率不高,则基数排序可能比合并排序和快速排序等其他排序算法慢。这些操作包括子列表的插入和删除功能以及隔离所需数字的过程。
- 基数排序不如其他排序灵活,因为它取决于数字或字母。如果更改了数据类型,则需要重写基数排序。
- 它不是就地排序算法,因为它需要额外的额外空间。