算法-基数排序
前置知识
思路
都将基数排序视为最快的排序算法(对整数而言)。
这是一个很鬼畜的算法,请做好心理准备。。。
我们现在有一个序列,怎么对它排序?
这是一个非常经典的问题,这里我们使用一个经典的基础算法——基数排序解决。
为了体现基数排序的方式,我们使用多位数。
这里有一个多位数序列,要对其升序排序。
654
784
137
914
261
359
\begin{array}{cc} 654&784&137&914&261&359 \end{array}
654784137914261359
我们从个位开始考虑,因为越往后的考虑更有决定性,所以从低位向高位考虑。
首先按照个位排序。
261
654
784
914
137
359
\begin{array}{cc} 261&654&784&914&137&359 \end{array}
261654784914137359
再按十位排序,但是需要保证排序具有稳定性,即该位相同时必须以元素原来在数组中的顺序排序。
914
137
654
359
261
784
\begin{array}{cc} 914&137&654&359&261&784 \end{array}
914137654359261784
最后按百位排序
137
261
359
654
784
914
\begin{array}{cc} 137&261&359&654&784&914 \end{array}
137261359654784914
排序完成。
那么借助计数排序的思想,我们利用数组来处理每一位。
例如将下面的序列按十位升序。
621
391
722
194
337
118
\begin{array}{cc} 621&391&722&194&337&118 \end{array}
621391722194337118
我们进行计数。
0
1
2
3
4
5
6
7
8
9
0
1
2
1
0
0
0
0
0
2
\begin{array}{cc} 0&1&2&3&4&5&6&7&8&9\\ \gray0&1&2&1&\gray0&\gray0&\gray0&\gray0&\gray0&2 \end{array}
00112231405060708092
对计数数组做前缀和。
0
1
2
3
4
5
6
7
8
9
0
1
3
4
4
4
4
4
4
6
\begin{array}{cc} 0&1&2&3&4&5&6&7&8&9\\ 0&1&3&4&4&4&4&4&4&6 \end{array}
00112334445464748496
不难发现,第
i
i
i 个元素实际上为十位为
i
i
i 的最后一个元素排序后的位置,故从后向前遍历原数组,每次放在第
i
i
i 个元素标出的位置,并将该元素减
1
1
1。
故排序后得到:
118
621
722
337
391
194
\begin{array}{cc} 118&621&722&337&391&194 \end{array}
118621722337391194
复制会原数组,完成。
一些卡常小技巧
基数排序不够快怎么办?凉拌炒鸡蛋,好吃又好看
下面为对几个
int
\text{int}
int 数组排序的方法。
std::sort()
- 2 16 2^{16} 216 跑两趟
- 2 11 2^{11} 211 跑三趟
- 2 8 2^8 28 跑四趟
- 10 10 10 跑十趟
CCF 评测机的 L1 缓存大小约为
2
13
2^{13}
213 个
int
\text{int}
int,所以建议
2
11
2^{11}
211 跑三趟。
但是我更喜欢写
2
8
2^8
28
实现代码
-
S
=
2
8
S=2^8
S=28 版本(
我更喜欢写这个)
void CountSort(int a[],int c[],int n,int w){
const int S=1<<8;
static int b[S];
for (int i=0;i<S;i++) b[i]=0;
for (int i=1;i<=n;i++) b[a[i]>>w&(S-1)]++;
for (int i=1;i<S;i++) b[i]+=b[i-1];
for (int i=n;i;i--) c[b[(a[i]>>w)&(S-1)]--]=a[i];
}
int tmp[MAXN];
void RadixSort(int a[],int n){
CountSort(a,tmp,n,0);
CountSort(a,tmp,n,8);
CountSort(a,tmp,n,16);
CountSort(a,tmp,n,24);
}