中复用来的类:
1 struct Student
2 {
3 char* number;
4 int score;
5 };
其中 number 表示学生学号,是一个十进制下8位数字的C风格字符串, score 的范围为0-660。那么, Student 类的对象就能表示成11个0-9的整数。为了让这个类支持基数排序,我们只需要写一个取相应位上的数的函数就可以了。如果排序的要求是分数从高到低,然后学号从小到大,那么这个函数对象可以实现为:
1 #include <stdexcept>
2
3 class StudentRadix
4 {
5 public:
6 static constexpr int radix = 10;
7 static constexpr int pass = 11;
8 unsigned operator()(const Student& _student, int _pass)
9 {
10 if (_pass < 8)
11 return _student.number[7 - _pass] - '0';
12 else switch (_pass)
13 {
14 case 8:
15 return 9 - _student.score % 10;
16 case 9:
17 return 9 - _student.score / 10 % 10;
18 case 10:
19 return 9 - _student.score / 100;
20 }
21 throw std::runtime_error("unknown \"pass\" value");
22 }
23 };
因为LSD基数排序是从低位到高位来分组的,所以较早的循环应该取的较次要的位;为了把分数排成降序,所有分数字段上的位都返回被9减去的结果。
同样地,调用处的代码为:
1 std::vector<Student> students;
2 radix_sort(students.begin(), students.end(), StudentRadix::radix, StudentRadix(), StudentRadix::pass);
对于更加简单的类型,比如0-999的整数,调用就更加简单(往往在这种情况下基数排序比时间复杂度为O(nlogn)的排序算法快):
1 std::vector<int> data;
2 radix_sort(data.begin(), data.end(), 10, [](int _value, int _pass) {
3 switch (_pass)
4 {
5 case 0: return _value % 10;
6 case 1: return _value / 10 % 10;
7 case 2: return _value / 100;
8 }
9 throw std::runtime_error();
10 }, 3);
最后还要说一下,以上接口和实现都还有优化的空间。对于接口,每一轮循环的基数可以不同,接口中可以加入这种考虑;对于实现,在分组与收集的过程中,每一轮循环,每一个元素都被复制了一次,对于涉及到动态内存的类来说,这是很耗时的,可以将基数排序与表排序结合使用来避免。
文章中如有错误请指正,并欢迎补充。