算法与数据结构——基数排序

基数排序

基数排序(radix sort)的核心思想与计数排序一致,也通过统计个数来实现排序。计数排序适用于数据量n较大但数据范围m比较小的情况。假设我们需要对n=106个学号进行排序,而学号是一个8位数字,这意味着数据范围m=108非常大,使用计数排序需要分配大量内存空间,而基数排序可以避免这种情况。

基数排序在计数排序的基础上,利用数字各位之间的递进关系,依次对每一位进行排序,从而得到最终的排序结果。

算法流程

以学号数据为例,假设数字的最低位是第1位,最高位是第8位。算法流程如下:

  1. 初始化位数k = 1。
  2. 对学号的第k位执行“计数排序”。完成后,数据根据第k位从小到大排序。
  3. 将k增加1, 然后返回步骤2.继续迭代,直到所有位都排序完成后结束。

下面剖析代码实现。对于一个d进制的数字x,哟啊获取其第k位xk,可以使用以下计算公式:

其中 ⌊a⌋ 表示对浮点数a向下取整,而 mod d表示对d取模(取余)。对于学号数据,d=10且k ∈ [1, 8]。

此外,我们需要小幅度改动计数排序代码,是之可以根据数字的第k位进行排序:

/* 获取元素num的第k位 其中 exp = 10^(k - 1) */
int digit(int num, int exp){
	// 传入exp而非k 可以避免在此重复执行昂贵的次方运算
	return (num / exp) % 10;
}
/* 计数排序(根据num的第k位进行排序) */
void countingSortDigit(vector<int> &nums, int exp){
	// 10进制的位范围是0~9,因此需要长度为10的桶数组
	vector<int> counter(10, 0);
	int n = nums.size();
	// 统计0~9各数字的出现次数
	for (int i = 0; i < n; ++i){
		int d = digit(nums[i], exp);		// 获取nums[i]的第k位,记为d
		counter[d]++;						// 统计数字d的出现次数
	}
	// 求前缀和,将“出现个数”转换为“数组索引”
	for (int i = 1; i < 10; ++i){
		counter[i] += counter[i - 1];
	}
	// 倒序遍历,根据桶内统计结果,将各元素填入res
	vector<int> res(n, 0);
	for (int i = n - 1; i >= 0; --i){
		int d = digit(nums[i], exp);
		int j = counter[d] - 1;		// 获取d在数组中的索引 j
		res[j] = nums[i];					// 将当前元素填入索引 j
		counter[d]--;						// 将d的数量减1
	}
	// 使用结果覆盖原数组
	for (int i = 0; i < n; ++i){
		nums[i] = res[i];
	}
}
/* 基数排序 */
void radixSort(vector<int> &nums){
	// 获取数组最大元素,用于判断最大位数
	int m = *max_element(nums.begin(), nums.end());
	// 按照从低位到高位的顺序遍历
	for (int exp = 1; exp < m; exp *= 10){
		countingSortDigit(nums, exp);
	}
}

为什么从最低位开始排序?
在连续的排序轮次中,后一轮排序会覆盖前一轮排序的结果。举例来说,如果第一轮排序结果a < b,而第二轮排序结果a > b,那么第二轮的结果将取代第一轮的结果。由于数字的高位优先级高于低位,因此应该先排序低位再排序高位。

算法特性

相较于计数排序,基数排序适用于数值范围比较大的情况,但前提是数字必须是可以表示为固定位数的格式,且位数不能过大。例如,浮点数不适合使用基数排序,因为其位数k过大,可能导致时间复杂度O(nk)≫O(n2)。

  • 时间复杂度O(nk):设数据量为n、数据为d进制、最大位数为k,则对某一位执行计数排序使用O(n+d)时间,排序所有k位使用O((n+d)k)时间。通常情况下,d和k都相对较小,时间复杂度趋向O(n)。
  • 空间复杂度O(n+d)、非原地排序:与计数排序相同,基数排序需要借助长度为n和d的数组rescounter
  • 稳定排序:当计数排序稳定时,基数排序也稳定;当计数排序不稳定时,基数排序无法保证得到正确的排序结果。
posted @ 2024-11-05 17:07  风陵南  阅读(1)  评论(0编辑  收藏  举报