O(n)时间的排序---计数排序
2012-07-04 15:05
183 查看
题目:某公司有几万名员工,请完成一个时间复杂度为O(n)的算法对该公司员工的年龄作排序,可使用O(1)的辅助空间。
分析:排序是面试时经常被提及的一类题目,我们也熟悉其中很多种算法,诸如插入排序、归并排序、冒泡排序,快速排序等等。这些排序的算法,要么是O(n2)的,要么是O(nlogn)的。可是这道题竟然要求是O(n)的,这里面到底有什么玄机呢?
题目特别强调是对一个公司的员工的年龄作排序。员工的数目虽然有几万人,但这几万员工的年龄却只有几十种可能。上班早的人一般也要等到将近二十岁才上班,一般人再晚到了六七十岁也不得不退休。
由于年龄总共只有几十种可能,我们可以很方便地统计出每一个年龄里有多少名员工。举个简单的例子,假设总共有5个员工,他们的年龄分别是25、24、26、24、25。我们统计出他们的年龄,24岁的有两个,25岁的也有两个,26岁的一个。那么我们根据年龄排序的结果就是:24、24、25、25、26,即在表示年龄的数组里写出两个24、两个25和一个26。
想明白了这种思路,我们就可以写出如下代码:
在上面的代码中,允许的范围是0到99岁。数组timesOfAge用来统计每个年龄出现的次数。某个年龄出现了多少次,就在数组ages里设置几次该年龄。这样就相当于给数组ages排序了。该方法用长度100的整数数组辅助空间换来了O(n)的时间效率。由于不管对多少人的年龄作排序,辅助数组的长度是固定的100个整数,因此它的空间复杂度是个常数,即O(1)。
以上转自何海涛博客
《算法导论》中文版第八章第二节 计数排序 里面有关于这个算法的详细讲解。根据算法导论所说,计数排序的一个重要特性就是它的稳定性,上面的算法似乎没有体现出来。根据《算法导论》,我用c++实现了书上的例子。
分析:排序是面试时经常被提及的一类题目,我们也熟悉其中很多种算法,诸如插入排序、归并排序、冒泡排序,快速排序等等。这些排序的算法,要么是O(n2)的,要么是O(nlogn)的。可是这道题竟然要求是O(n)的,这里面到底有什么玄机呢?
题目特别强调是对一个公司的员工的年龄作排序。员工的数目虽然有几万人,但这几万员工的年龄却只有几十种可能。上班早的人一般也要等到将近二十岁才上班,一般人再晚到了六七十岁也不得不退休。
由于年龄总共只有几十种可能,我们可以很方便地统计出每一个年龄里有多少名员工。举个简单的例子,假设总共有5个员工,他们的年龄分别是25、24、26、24、25。我们统计出他们的年龄,24岁的有两个,25岁的也有两个,26岁的一个。那么我们根据年龄排序的结果就是:24、24、25、25、26,即在表示年龄的数组里写出两个24、两个25和一个26。
想明白了这种思路,我们就可以写出如下代码:
void SortAges(int ages[], int length) { if(ages == NULL || length <= 0) return; const int oldestAge = 99; int timesOfAge[oldestAge + 1]; for(int i = 0; i <= oldestAge; ++ i) timesOfAge[i] = 0; for(int i = 0; i < length; ++ i) { int age = ages[i]; if(age < 0 || age > oldestAge) throw new std::exception("age out of range."); ++ timesOfAge[age]; } int index = 0; for(int i = 0; i <= oldestAge; ++ i) { for(int j = 0; j < timesOfAge[i]; ++ j) { ages[index] = i; ++ index; } } }
在上面的代码中,允许的范围是0到99岁。数组timesOfAge用来统计每个年龄出现的次数。某个年龄出现了多少次,就在数组ages里设置几次该年龄。这样就相当于给数组ages排序了。该方法用长度100的整数数组辅助空间换来了O(n)的时间效率。由于不管对多少人的年龄作排序,辅助数组的长度是固定的100个整数,因此它的空间复杂度是个常数,即O(1)。
以上转自何海涛博客
《算法导论》中文版第八章第二节 计数排序 里面有关于这个算法的详细讲解。根据算法导论所说,计数排序的一个重要特性就是它的稳定性,上面的算法似乎没有体现出来。根据《算法导论》,我用c++实现了书上的例子。
#include <iostream> using namespace std; void CountingSort(int ages[], int len, int out[], int range) { int *count = new int[range+1]; for(int i=0;i<=range;++i) { count[i]=0; } for(int i=0;i<len;++i) { count[ages[i]]++; } for(int i=1;i<=range;++i) { count[i] += count[i-1]; } for (int i=len-1;i>=0;--i) { out[count[ages[i]]-1] = ages[i]; count[ages[i]]--; } } int main() { const int age_range = 99; int a[] = {2,5,3,0,2,3,0,3}; int b[8]; CountingSort(a,8,b,age_range); for(int i=0;i<8;i++) { cout << b[i] << "\t"; } cout << endl; system("pause"); return 0; }
相关文章推荐
- 第八章 线性时间排序 8.2 计数排序
- 线性时间排序之计数排序(JAVA)
- 时间为O(n)排序——计数排序
- 《算法导论》第8章 线性时间排序 (1)计数排序
- 算法导论:第8章 线性时间排序__计数排序
- 《算法导论》第8章 线性时间排序 (1)计数排序
- O(n)时间的排序---计数排序
- 线性时间排序之计数排序,基数排序和桶排序
- 《算法导论》第8章 线性时间排序 (1)计数排序
- 第八章 线性时间排序 8.2 计数排序
- 计数排序,基数排序,桶排序和快速排序算法时间对比
- 【算法12】时间为O(n)排序——计数排序
- 排序算法(五)-- 线性时间排序之计数排序
- 《算法导论》第8章 线性时间排序 (1)计数排序
- 线性时间排序:计数排序、计数排序、桶排序
- [算法学习]线性时间排序:计数排序、基数排序和桶排序
- 线性时间排序之计数排序(算法导论8.2)
- 算法导论 第8章 线性时间排序-计数排序的原址排序
- 牺牲空间换时间的非比较排序之计数排序和基数排序
- 【算法学习】线性时间排序-计数排序、基数排序和桶排序详解与编程实现