线性时间排序——计数排序,基数排序,桶排序

线性时间排序即时间复杂度为Θ(n)的排序,主要有计数排序、基数排序和桶排序 
以前的排序都涉及到元素比较,称为比较排序,渐近最优的为 随机快速排序(快速排序在平均情况下),merge sort归并排序和heap sort,时间复杂度为Θ(nlogn) 

而这种排序都不是用比较的操作来排序,所以下届Θ(nlogn)对它们不适用 

一、计数排序

时间复杂度为Θ(n+k),k为常数 
计数排序适合所需排序的数组元素取值范围不大的情况,要求取值范围小于元素个数

#include <stdio.h> 
#include <stdlib.h>  

//计数排序 
int CountSort(int* pData, int nLen) 
{    
int* pCout = NULL;         //保存记数数据的指针     
pCout = (int*)malloc(sizeof(int) * nLen);  //申请空间     
//初始化记数为0   
for (int i = 0; i < nLen; ++i)     
{    
   pCout[i] = 0;     
}
//记录排序记数。在排序的值相应加一   
for(int i = 0; i < nLen; ++i)     
{     
     ++pCout[pData[i]];        //增     
}    
//确定不比该位置大的数据个数
for (int i = 1; i < nLen; ++i)     
{
pCout[i] += pCout[i - 1];
//不比他大的数据个数为他的个数加上前一个的记数。     
}     
int* pSort = NULL;         //保存排序结果的指针pSort=(int*)malloc(sizeof(int) * nLen);    //申请空间  
for (int i = 0; i < nLen; ++i)     
{ 
//把数据放在指定位置。因为pCout[pData[i]]的值就是不比他大数据的个数
//为什么要先减一,因为pCout[pData[i]]保存的是不比他大数据的个数中包括了
//他自己,我的下标是从零开始的!所以要先减一。 
--pCout[pData[i]];//因为有相同数据的可能,所以要把该位置数据个数减一。
pSort[pCout[pData[i]]] = pData[i]; 
}
//排序结束,复制到原来数组中
for (int i = 0; i < nLen; ++i)     
{      
pData[i] = pSort[i];     
}  
    //最后要注意释放申请的空间。     
free(pCout);     
free(pSort);

 return 1;
 }   

int main() {      
int nData[10] = {8,6,3,6,5,8,3,5,1,0};     
CountSort(nData, 10);     
for (int i = 0; i < 10; ++i)     
{          
printf("%d ", nData[i]);     
}     
 printf("\n");       
system("pause");     
return 0; 
} 
  

二、基数排序

1,假设数组a所有元素i为十进制整数且位数不超过d位 
2,递归对数组a的个位、十位、...、d位排序 
当没位数字都界于1到k之间时,对n个d位数的每一位处理的时间为Θ(n+k),共d次处理,所以时间复杂度为Θ(dn+dk),d为常数 
基数排序分LSD(Least significant digital)和MSD(Most significant digital),前者从最低位开始排,后者从最高位开始排 
LSD需要保持较低位的位置,而MSD则不需要 

#include <stdio.h> 
#include <stdlib.h>  
//计数排序,npRadix为对应的关键字序列,nMax是关键字的范围。npData是具体要 
//排的数据,nLen是数据的范围,这里必须注意npIndex和npData对应的下标要一致 
//也就是说npIndex[1] 所对应的值为npData[1] 

int RadixCountSort(int* npIndex, int nMax, int* npData, int nLen) 
{ 
    //这里就不用说了,计数的排序。不过这里为了是排序稳定     
    //在标准的方法上做了小修改。
    int* pnCount  = (int*)malloc(sizeof(int)* nMax);        //保存计数的个数     
    for (int i = 0; i < nMax; ++i)    
   { 
        pnCount[i] = 0;     
   } 
    for (int i = 0; i < nLen; ++i)    //初始化计数个数    
   { 
        ++pnCount[npIndex[i]];    
   }  
    for (int i = 1; i < 10; ++i)  //确定不大于该位置的个数。     
  { 
   pnCount[i] += pnCount[i - 1];    
   }  
    int * pnSort  = (int*)malloc(sizeof(int) * nLen);    //存放零时的排序结果。  
    //注意:这里i是从nLen-1到0的顺序排序的,是为了使排序稳定。    
   for (int i = nLen - 1; i >= 0; --i)     
  { 
        --pnCount[npIndex[i]];         
        pnSort[pnCount[npIndex[i]]] = npData[i];     
   }
   for (int i = 0; i < nLen; ++i)        //把排序结构输入到返回的数据中。     
  { 
        npData[i] = pnSort[i];     
  } 
    free(pnSort);                        //记得释放资源。     
    free(pnCount);    
     return 1; 
}  

//基数排序 
int RadixSort(int* nPData, int nLen)
 { 
        //申请存放基数的空间
    int* nDataRadix    = (int*)malloc(sizeof(int) * nLen);
    int nRadixBase = 1;    //初始化倍数基数为1     
    bool nIsOk = false; //设置完成排序为false  
    	//循环,知道排序完成     
      while (!nIsOk)     
      { 
  	 nIsOk = true;         
  	 nRadixBase *= 10; 
  	 for (int i = 0; i < nLen; ++i)         
 	{ 
   	  nDataRadix[i] = nPData[i] % nRadixBase;            
  	   nDataRadix[i] /= nRadixBase / 10;             
   	  if (nDataRadix[i] > 0)
   	    { 
                nIsOk = false;            
   	    }        
         } 
  	 if (nIsOk)        //如果所有的基数都为0,认为排序完成,就是已经判断到最高位了。         
   	{ 
     	   break;        
	} 
	RadixCountSort(nDataRadix, 10, nPData, nLen);    
      }  
    free(nDataRadix); 
    return 1; 
}  

int main() 
{ 
    //测试基数排序。 
    int nData[10] = {123,5264,9513,854,9639,1985,159,3654,8521,8888};     
    RadixSort(nData, 10);     
    for (int i = 0; i < 10; ++i)     
    { 
        printf("%d ", nData[i]);    
    } 
    printf("\n");
    system("pause");     
    return 0; 
}

三、桶排序

桶排序是另外一种以O(n)或者接近O(n)的复杂度排序的算法. 它假设输入的待排序元素是等可能的落在等间隔的值区间内.一个长度为N的数组使用桶排序, 需要长度为N的辅助数组. 等间隔的区间称为桶, 每个桶内落在该区间的元素. 桶排序是基数排序的一种归纳结果。

算法的主要思想: 待排序数组A[1...n]内的元素是随机分布在[0,1)区间内的的浮点数.辅助排序数组B[0....n-1]的每一个元素都连接一个链表.将A内每个元素乘以N(数组规模)取底,并以此为索引插入(插入排序)数组B的对应位置的连表中. 最后将所有的链表依次连接起来就是排序结果.

这个过程可以简单的分步如下:

  1. 设置一个定量的数组当作空桶子。
  2. 寻访序列,并且把项目一个一个放到对应的桶子去。
  3. 对每个不是空的桶子进行排序。
  4. 从不是空的桶子里把项目再放回原来的序列中。

note: 待排序元素越均匀, 桶排序的效率越高. 均匀意味着每个桶在中间过程中容纳的元素个数都差不多,不会出现特别少或者特别多的情况, 这样在排序子程序进行桶内排序的过程中会达到最优效率.(否则效率并不理想)

note: 将元素通过恰当的映射关系将元素尽量等数量的分到各个桶(值区间)里面, 这个映射关系就是桶排序算法的关键.桶的标记(数组索引Index)的大小也要和值区间有对应关系



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值