您的位置:首页 > 其它

程序猿找工作必练内功:排序算法大总结(五)——线性时间排序算法(计数排序,基数排序,桶排序)

2013-12-31 23:03 417 查看
前几篇关于排序的博客中所讲的排序算法都是基于比较的,他们的时间复杂度的下界都是Ω(nlogn),这些算法对原始数据的分布没有要求。下面所说的几种O(n)时间复杂度的排序算法,它们都对原始数据的分布都有一定的假设,在假设成立的情况下,O(n)才成立。

首先说一下计数排序算法,该算法对输入的数据进行分类统计,然后再确定每个输入数据在输出时的位置,从而达到排序的目的。该算法得明确知道输入数据的取值范围,否则效率将很低,而且数据分布越均匀越好。设数据的个数为n,取值范围的区间长度为k,且满足k=O(n)时,该算法的时间复杂度才为O(n)。

基数排序算法是一种多关键字排序算法,每个数据具有多个关键字,各个关键字之间有确定的先后关系。基数排序从最低的关键字开始排序每个数据,依次到最高的关键字。比如一个三位数的数字数组,先按个位排序数组,再按十位,最后按百位,等所有关键字排完后,整个数组就排好序了。基数排序的时间复杂度是O(d(n+k)),其中d是每个关键字的位数(假设是整型值),n是关键字的个数,k是关键字每位的取值范围,当d和k确定时,基数排序的时间复杂度就是O(n)。

最后说桶排序,该算法假设数据要均匀分布在某个确定的范围内,然后将该范围划分为等概率的多个区间,即要求数据能够均匀的分布在每个区间内,这些区间就称为桶。根据自定的某种映射办法将数据映射到属于自己的桶中,然后使用其他排序算法排序每个桶中的数据,最后再依次输出桶中的数据即可完成所有数据的排序。桶排序的关键在于保证每个桶中的数据个数的平方和为O(n),n是数据的个数,因此,设计合适的桶就是排序的关键,也是该算法时间复杂度为O(n)的保证。

下面给出这三种算法的实现

#ifndef COUNTSORT_HPP_INCLUDED
#define COUNTSORT_HPP_INCLUDED
/**< 计数排序 */
template<class Type>
void countSort(Type *A, int n, int k)
{
try
{
int *c = new int[k+1];   //统计每种元素的个数
Type *B = new Type
;   //保存输出结果
for(int i=0; i<=k; i++)
c[i]=0;
for(int i=0; i<n; i++)    //统计
c[A[i]]++;
for(int i=1; i<=k; i++)    //确定<=i的元素的个数 ,即在输出数组中的位置
c[i] += c[i-1];
for(int i=n-1; i>=0; i--)   //输出
{
B[c[A[i]]-1] =  A[i]; //注意个数和下标的关系
c[A[i]]--;
}
for(int i=0; i<n; i++)
A[i] = B[i];
delete []c;
delete []B;
}
catch(bad_alloc e)
{
return;
}

}

#endif // COUNTSORT_HPP_INCLUDED


//基数排序
#ifndef RADIXSORT_HPP_INCLUDED
#define RADIXSORT_HPP_INCLUDED

#include "CountSort.hpp"

/** \brief  取整数n的从低到高的第m位
*
* \param n:整数
* \param m:指定的位
* \return int:第m位的数字
*
*/

int getBit(int n,int m)
{
while(m>1)
{
n /= 10;
m--;
}
return n%10;
}
/** \brief 按照k进制整数序列A的第m位的大小排序A,使用计数排序法
*
* \param  A:整数序列
* \param  n:序列长度
* \param  k:进制
* \param  m:第m位
* \return
*
*/

void countSort(int *A, int n, int k, int m)
{
try
{
int *c = new int[k];
int *B = new int
;
char *bits = new char
;
for(int i=0; i<k; i++)
c[i]=0;
for(int i=0; i<n; i++)
bits[i] = getBit(A[i], m);
for(int i=0; i<n; i++)
c[bits[i]]++;
for(int i=1; i<k; i++)
c[i] += c[i-1];
for(int i=n-1; i>=0; i--)
{
B[c[bits[i]]-1] =  A[i];
c[bits[i]]--;
}
for(int i=0; i<n; i++)
A[i] = B[i];
delete []c;
delete []B;
delete []bits;
}
catch(bad_alloc)
{
return;
}
}
/** \brief 基数排序
*
* \param A:整数序列指针
* \param d:整数的长度
* \param n:整数的个数
* \param k:进制(<=10)
* \return
*
*/

void radixSort(int *A, int d, int n, int k)
{
for(int i=1; i<=d; i++)
countSort(A, n, k, i); //从最低位开始排序,必须使用稳定的排序算法
}
#endif // RADIXSORT_HPP_INCLUDED


/**< 桶排序中,只要所有桶的大小的平方和与元素个数呈线性关系,
即可在线性时间内完成排序 */

#ifndef BUCKETSORT_H_INCLUDED
#define BUCKETSORT_H_INCLUDED

#include<vector>
#include<algorithm>

using namespace std;

/** \brief 序列一般满足均匀分布在某个范围内即可实现线性排序 ,
实践中得根据不同问题设计合适的桶,此处拿浮点型来举例
*
* \param  A:浮点型序列指针
* \param  n:序列长度
* \param  start:序列元素的下界(>=1)
* \param  end:序列元素的上界(>=1)
* \return
*
*/
void bucketSort(float *A, int n, int start, int end)
{
try
{
int bucketCount = end-start+1; //桶的个数
vector<vector<float> > buckets;  //创建桶

for(int i=0; i<bucketCount; i++)
buckets.push_back(vector<float>());

for(int i=0; i<n; i++)
buckets[(int)(A[i]-start)].push_back(A[i]);//把元素加入到数据自己的桶中

for(vector<vector<float> >::iterator it = buckets.begin();
it!=buckets.end(); it++)
sort(it->begin(), it->end());//排序每个桶中的元素

int k=0;
for(vector<vector<float> >::iterator it = buckets.begin();
it!=buckets.end(); it++) //依次输出每个桶中的元素
{
for(vector<float>::iterator it2=it->begin();
it2!=it->end(); it2++)
A[k++] = *it2;
}
}
catch(bad_alloc e)
{
return;
}

}

#endif // BUCKETSORT_H_INCLUDED
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: