桶排序(计数排序)
算法原理
桶排序是设置m
个桶,每个桶记录从0到N中的单个元素出现的次数,即第i
个桶记录数字i
出现的次数,然后将桶里的数放到数组中即可。
那么如何将桶里的数放到原数组中呢?首先我们知道,1号桶的元素一定都在2号桶前面,假如有10个1和1个2,那么2的下标10 + 2 - 1,因此我们只需要算出每个数对应的前缀和,然后就能求出每个数对应的下标了。
注意:因为是按顺序遍历,因此同一个数中越靠后的数会在越上面,例如1,1'
,s[1] = 1
时放入的是1
,s[1] = 2
时放入的是1'
,因此在将桶里的数放入数组时我们需要从后往前遍历确保相同元素的相对位置不变。
代码
void backet_sort(){ // 桶排序(计数排序)
for(int i = 0; i < n; i ++) s[q[i]] ++; // 将每个元素放到对应桶里
for(int i = 1; i < N; i ++) s[i] += s[i - 1]; // 求出每个元素的前缀和,即可得到每个元素排序后的位置
for(int i = n - 1; i >= 0;i --) w[-- s[q[i]]] = s[q[i]]; // 因为遍历时相同元素靠后的会被放在上面,所以从后往前遍历可以确保相同元素相对位置不变
for(int i = 0; i < n; i ++) q[i] = w[i]; // 将数组复制回原数组
}
时间复杂度
最好情况: O ( n + m ) O(n + m) O(n+m)
平均情况: O ( n + m ) O(n + m) O(n+m)
最坏情况: O ( n + m ) O(n + m) O(n+m)
空间复杂度
O ( n + m ) O(n + m) O(n+m)
稳定
基数排序
算法原理
基数排序是桶排序的一个优化,因为当数值达到一定大小后(例如a[i] ==
1
0
9
10^9
109),桶排序的时间复杂度达到了
1
0
9
10^9
109(需要安排
1
0
9
10^9
109个桶),显然是无法满足我们的需求的,因此基数排序就是将需要排序的数字转化为r进制
,此时只需要r
个桶,假设数组元素a[i]
转换为r进制
后最高有d
位,那么只需要对位数从小到大进行桶排序,即可在
O
(
d
(
n
+
r
)
)
O(d(n + r))
O(d(n+r))的时间复杂度内完成排序,因为
d
=
⌈
l
o
g
r
n
⌉
d = \lceil log_rn\rceil
d=⌈logrn⌉,且r比较小,因此我们可以近似认为时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
代码
void radix_sort(int d,int r){ // 基数排序,d代表最高位数,r代表进制数
int radix = 1; // 用来取出每个数的第i为数字
for(int i = 1; i <= d; i ++){ // 从个位开始排序
for(int j = 0; j < r; j ++) s[j] = 0; // 清空桶
for(int j = 0; j < n; j ++) s[q[j] / radix % r] ++; // 对第i位数进行桶排序
for(int j = 1; j < r; j ++) s[j] += s[j - 1];
for(int j = n - 1; j >= 0; j --) w[-- s[q[j] / radix % r]] = q[j];
for(int j = 0; j < n; j ++) q[j] = w[j];
radix *= r; // 进入下一位的桶排序
}
}
时间复杂度
最好情况: O ( d ( n + r ) ) O(d(n + r)) O(d(n+r))
平均情况: O ( d ( n + r ) ) O(d(n + r)) O(d(n+r))
最坏情况: O ( d ( n + r ) ) O(d(n + r)) O(d(n+r))
空间复杂度
O ( n + r ) O(n + r) O(n+r)