基数排序
前言
通常数据结构和算法课程中会涉及到多种排序算法,一般有冒泡排序、简单插入排序、选择排序、希尔排序、归并排序、堆排序、快速排序等,时间复杂从O(n)降至O(nlogn)。这次介绍的基数排序和前文介绍的各种排序算法最大的不同在于时间复杂度仅为O(n)。
前提条件
我们为了讲清楚这个排序算法,假设待排序的数为非负数(大于等于零),取值范围为int类型(32位)。
基本思路
首先我们需要开辟10个桶,以供比较之用。
由于假设待比较的数字只有32位,那么我们最多只需要比较10轮,每轮比较后将数字按序存放至空数组,作为下一轮比较的输入,直到各轮次比较结束,完成排序。
案例分析
待比较的数组 nums = [78,34,67,9,347,8,1,2,3,0,320];
第一步:待比较的数组中最大的位数为3,因此只需要比较3轮。
第二步:开辟10块空间(桶),存放比较结果。
第三步:第一轮比较个位,比较后十个桶分别存放的数为:
0 :[0,320]
1 :[1]
2 :[2]
3 :[3]
4 :[34]
5 :[]
6 :[]
7 :[67,347]
8 :[78,8]
9 :[9]
将结果汇总此时nums1 = [0,320,1,2,3,34,67,347,78,8,9]
第二轮比较十位,nums1作为输入,比较后桶内各数的分布为:
0 : [0,1,2,3,8,9]
1 : []
2 : [320]
3 : [34,347]
4 : []
5 : []
6 : [67]
7 : [78]
8 : []
9 : []
结果汇总 nums2 = [0,1,2,3,8,9,320,34,347,67,78]
第三轮以此类推 比较百位,一共比较3轮。
得到结果 nums3 = [0,1,2,3,8,9,34,67,78,320,347]
代码展示
#include <bits/stdc++.h>
using namespace std;
void radixSort(vector<int>& nums){
// 为了简单起见,假设数字为非负数,int 类型
// len:表示需要排序的轮数
// 也就是待排序数字的最大位数
int len = 0,n = nums.size();
for (int i = 0;i < n;++i){
int temp = nums[i];
int total = 0;
while (temp){
++total;
temp >>= 1;
}
len = max(len,total);
}
// 开辟10个空间(桶)
vector<vector<int>> bucket(10);
int base = 1;
while (len){
bucket.clear();
bucket.resize(10);
for (int i = 0;i < n;++i){
bucket[(nums[i] / base) % 10].push_back(nums[i]);
}
nums.clear();
for (int i = 0;i < 10;++i){
for (int j = 0;j < (int)bucket[i].size();++j){
nums.push_back(bucket[i][j]);
}
}
--len;
base *= 10;
}
return ;
}
int main(void){
vector<int> nums{78,34,67,9,347,8,1,2,3,0,320};
radixSort(nums);
for (int i = 0;i < (int)nums.size();++i) cout << nums[i] << " ";
return 0;
}
问题变种
可以将待比较数字延伸至负数域,数位范围可以扩展到更大位数,还存在高位先比较、低位后比较的算法,可以自行搜索。
小记
某些情况下,基数排序会比快速排序效果更好。