算法-计数排序
前置知识
- 数组
- STL 中的
map
类型
思路
这是一个很鬼畜的算法,请做好心理准备。。。
我们现在有一个序列,怎么对它排序?
这是一个非常经典的问题,这里我们使用一个经典的基础算法——计数排序解决。
我们有一个序列,要进行升序排序。
7
3
1
7
4
3
\begin{array}{cc} 7&3&1&7&4&3 \end{array}
731743
我们建立一个数组,为对应每个数出现的次数。
依次用序列元素更新数组。
最后,我们得到下面这个数组:
1
2
3
4
5
6
7
1
0
2
1
0
0
2
\begin{array}{cc} 1&2&3&4&5&6&7\\\\ 1&\gray0&2&1&\gray0&\gray0&2 \end{array}
11203241506072
然后从小到大,在答案序列中加入相应个数的元素。
1
3
3
4
7
7
\begin{array}{cc} 1&3&3&4&7&7 \end{array}
133477
完成
算法参数
- 平均时间复杂度: O ( n ) O(n) O(n)
- 最好时间复杂度: O ( n ) O(n) O(n)
- 最坏时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( max ( a ) ) O(\max(a)) O(max(a))
极端情况
如果数据过大,计数排序的数组可能开不下。
比如:
2000000000
1000000000
\begin{array}{cc} 2000000000&1000000000 \end{array}
20000000001000000000
显然不可能将数组开到
2000000000
2000000000
2000000000。
优化
我们考虑使用 STL 中的 map
类型来偷懒解决。
只需要使用 iterator
遍历 map
就可以做到
O
(
n
log
n
)
O(n\log n)
O(nlogn) 了。
但是 map
是使用红黑树维护的,所以相当于堆排序
算法参数
- 平均时间复杂度: O ( n log n ) O(n\log n) O(nlogn)
- 最好时间复杂度: O ( n log n ) O(n\log n) O(nlogn)
- 最坏时间复杂度: O ( n log n ) O(n\log n) O(nlogn)
- 空间复杂度: O ( n ) O(n) O(n)
实现代码
- 基础版本
void CountSort(int a[],int n){//计数排序
int mx=a[1],mn=a[1];//最大与最小元素
for (int i=2;i<=n;i++) mx=max(mx,a[i]),mn=min(mn,a[i]);
int tmp[mx-mn+10];//计数数组
for (int i=0;i<=mx-mn;i++) tmp[i]=0;//初始化
for (int i=1;i<=n;i++) tmp[a[i]-mn]++;//将数量加1
int k=0;//数组指针
for (int i=0;i<=mx-mn;i++)//遍历数组
for (int j=1;j<=tmp[i];j++)
a[++k]=i+mn;//取出
}
- 优化版本
void CountSort(int a[],int n){//计数排序
map<int,int> tmp;//计数数组
for (int i=1;i<=n;i++)
tmp[a[i]]++;//将数量加1
map<int,int>::iterator it;//map指针
int k=0;//数组指针
for (it=tmp.begin();it!=tmp.end();it++)//遍历map
for (int i=1;i<=it->second;i++)
a[++k]=it->first;//取出
}