经典排序算法分析和代码-下篇

来源:互联网 发布:西贝柳斯软件谱子扫描 编辑:程序博客网 时间:2024/05/20 21:21

这篇文档我们来讲两种非比较排序,计数排序,基数排序

计数排序

计数排序是一种非比较算法,其时间复杂度为O(N+K)

 

举例说明

先用一个例子来说明计数排序算法,比如需要排序的集合为{1, 2, 1, 0, 4, 5},在该集合中,最大的数值为5,那么通过遍历整个集合,

可以得到这样的数组

   int counter[] = {1, 2, 1, 0, 1, 1}

                              0, 1, 2, 3, 4, 5

counter数组描述了被排序数组里有1个0, 2个1, 1个2, 0个3, 1个4和1个5,当这个数组形成时,排序也就结束了。

 

代码设计

1)根据集合中最大的数值K,来申请辅助空间counter数组

2)遍历被排序集合,讲计数记录到counter数组中

 

代码实现

#include <stdio.h>#include <stdlib.h>#include <string.h>int data[] = {1, 2, 1, 0, 4, 5};// 计数排序参数列表// int d[] 为待排序数据列表// int n 为待排序数据个数// int min, max为待排序数据中最大和最小的数,通过其计算待排数据跨度Kvoid sort_counter(int d[], int n, int k){    int i, j = 0;    k++; // 实际申请空间比K大1    // 申请辅助空间    int* counter = malloc(sizeof(int)*k);    memset(counter, 0, sizeof(int)*k);    // 计数    for(i=0; i<n; ++i)    {        ++counter[d[i]];    }    // 讲计数结果保存到待排数据空间    for(i=0; i<k; ++i)    {        while(counter[i]-- > 0)        {            d[j++] = i;        }    }    // 释放辅助空间    free(counter);}int main(void){    sort_counter(data, 6, 5);    int i;    for(i=0; i<6; ++i)    {        printf("%d\n", data[i]);    }    return 0;}




特点

计数排序的特单是时间复杂度,与其他的排序算法不同,它的时间复杂度为O(N+K),这个时间复杂度表明了当K相对比较大时,不适合使用,比如对集合{1, 0, 100}

但是对于N远大于K的情况下,是相当适合的

 

基数排序

在数量大时,计数排序需要大量的辅助空间,并且时间复杂度有可能比较大,所以推出基数排序。

举例说明

假如有待排序数据 data[] = {312, 213, 545, 893};

先排序个位数:排序结果为 312, 213, 893, 545

再排序十位数:排序结果为 321, 213, 545, 893

再排序百位数:排序结果为 213, 312, 545, 893

完毕

对位数的排序,可以使用计数排序,速度快而且稳定。

 

代码设计

1)获取待排序数据中的最大位数

2)对位数进行循环,按位对待排序数据进行计数排序,并将其中间结果保存到临时空间

3)将临时数据保存到待排序数据,继续步骤2)

 

代码实现

#include <stdio.h>// int data[] = {1, 100, 321, 121, 333, 586, 1100};// 该函数计算data中最大位数,本例子中,最大位数是1100,所以结果是4int maxbit(int data[],int n){    int d = 1; //保存最大的位数    int p =10;    for(int i = 0;i < n; ++i)    {        while(data[i] >= p)        {            p *= 10;            ++d;        }    }    return d;}// 基数排序void sort_radix(int data[],int n){    int d = maxbit(data, n);    // 计算位数    int * tmp = new int[n];     // 中间变量,用来存储中间排序结果    int * count = new int[10];  // 计数排序中的计数器    int i,j,k;    int radix = 1;    // 根据最大位数进行循环,对没一位进行计数排序    for(i = 1; i<= d;i++)    {        // 初始化计数器        for(j = 0; j < 10; j++)            count[j] = 0;        // 对位数进行计数排序        for(j = 0; j < n; j++)        {            k = (data[j]/radix)%10; // 注意这里进行取模            count[k]++;             // 计数        }        for(j = 1; j < 10; j++)            count[j] = count[j-1] + count[j];        // 将排序中间结果保存到tmp        for(j = n-1; j >= 0;j--)        {            k = (data[j]/radix)%10;            tmp[count[k]-1] = data[j];            count[k]--;        }        // 将中间结果保存到data        for(j = 0;j < n;j++)            data[j] = tmp[j];        // 取模时的被除数,需要提高一位        radix = radix*10;    }    delete [] tmp;    delete [] count;}int main(){    int data[] = {1, 100, 321, 121, 333, 586, 1100};    sort_radix(data, 7);    for(int i=0; i<7; i++)    {        printf("%d\n", data[i]);    }    return 0;}


特点

基数排序比较适合对取值很大的数进行排序,也可用来对字符串进行排序。

但基数排序的缺点是不呈现时空局部性,因为在按位对每个数进行排序的过程中,一个数的位置可能发生巨大的变化,所以不能充分利用现代机器缓存提供的优势。同时计数排序作为中间稳定排序的话,不具有原地排序的特点,当内存容量比较宝贵的时候,还是有待商榷。

 

原创粉丝点击