算法总结系列之五: 基数排序(Radix Sort)

基数排序是非比较排序算法,算法的时间复杂度是O(n). 相比于快速排序的O(nlgn),从表面上看具有不小的优势.但事实上可能有些出入,因为基数排序的n可能具有比较大的系数K.因此在具体的应用中,应首先对这个排序函数的效率进行评估.

基数排序的主要思路是,将所有待比较数值(注意,必须是正整数)统一为同样的数位长度,数位较短的数前面补零. 然后, 从最低位开始, 依次进行一次稳定排序(我们常用上一篇blog介绍的计数排序算法, 因为每个位可能的取值范围是固定的从0到9).这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列.

比如这样一个数列排序: 342 58 576 356, 以下描述演示了具体的排序过程(红色字体表示正在排序的数位)

第一次排序(个位):

3 4 2

5 7 6

3 5 6

0 5 8

第二次排序(十位):

3 4 2

3 5 6

0 5 8

5 7 6

第三次排序(百位):

0 5 8

3 4 2

3 5 6

5 7 6

结果: 58 342 356 576

两个问题:

  • 为什么要从低位开始向高位排序?

        如果要从高位排序, 那么次高位的排序会影响高位已经排好的大小关系. 在数学中, 数位越高,数位值对数的大小的影响就越大.从低位开始排序,就是对这种影响的排序. 数位按照影响力从低到高的顺序排序, 数位影响力相同则比较数位值.

  • 为什么同一数位的排序子程序要使用稳定排序?

        稳定排序的意思是指, 待排序相同元素之间的相对前后关系,在各次排序中不会改变.比如实例中具有十位数字5的两个数字58和356, 在十位排序之前356在58之前,在十位排序之后, 356依然在58之前.

        稳定排序能保证,上一次的排序成果被保留,十位数的排序过程能保留个位数的排序成果,百位数的排序过程能保留十位数的排序成果.

算法的C#实现(综合了计数排序算法作为数位内排序算法):

public static int[] RadixSort(int[] ArrayToSort, int digit)
{
    //low to high digit
    for (int k = 1; k <= digit; k++)
    {
        //temp array to store the sort result inside digit
        int[] tmpArray = new int[ArrayToSort.Length];
 
        //temp array for countingsort
        int[] tmpCountingSortArray = new int[10]{0,0,0,0,0,0,0,0,0,0};
 
        //CountingSort
        for (int i = 0; i < ArrayToSort.Length; i++)
        {
            //split the specified digit from the element
            int tmpSplitDigit = ArrayToSort[i]/(int)Math.Pow(10,k-1) - (ArrayToSort[i]/(int)Math.Pow(10,k))*10;
            tmpCountingSortArray[tmpSplitDigit] += 1; 
        }
 
        for (int m = 1; m < 10; m++)
        {
            tmpCountingSortArray[m] += tmpCountingSortArray[m - 1];
        }
 
        //output the value to result
        for (int n = ArrayToSort.Length - 1; n >= 0; n--)
        {
            int tmpSplitDigit = ArrayToSort[n] / (int)Math.Pow(10,k - 1) - (ArrayToSort[n]/(int)Math.Pow(10,k)) * 10;
            tmpArray[tmpCountingSortArray[tmpSplitDigit]-1] = ArrayToSort[n];
            tmpCountingSortArray[tmpSplitDigit] -= 1;
        }
 
        //copy the digit-inside sort result to source array
        for (int p = 0; p < ArrayToSort.Length; p++)
        {
            ArrayToSort[p] = tmpArray[p];
        }
    }
 
    return ArrayToSort;
}

 

这个算法的难度在于分离数位,将分离出的数位代表原元素的代表, 用作计数排序.但是分离数位不能脱离原来的数字,计数排序的结果,还是要移动原元素.

注意计数排序的元素数值与位置的联系,引申到基数排序的从元素得到中间值然后与位置的联系.

 

测试程序如下:

static void Main(string[] args)
{
    int[] ary = new int[] { 332, 653, 632, 755, 433, 722, 48 };
 
    int[] res = RadixSort(ary, 3);
 
    foreach (int vr in res)
    {
        Console.Write(vr.ToString() + " ");
    }
 
    Console.ReadLine();
}
posted @ 2008-06-26 01:04 SunShining 阅读(1710) 评论(11)  编辑 收藏

  回复  引用    
#1楼 2008-06-26 01:55 | 石旭湘 [未注册用户]
学习
  回复  引用    
#2楼 2008-06-26 02:40 | 全自动风淋室 [未注册用户]
再次关于基数排序问题。*
  回复  引用  查看    
#3楼 [楼主]2008-06-26 10:58 | Jian Sun      
写这个系列文章才知道,原来看懂理解与流畅的写出来的区别还是很大的.
  回复  引用  查看    
#4楼 2008-06-26 12:50 | 廉毅      
排列组合的算法有吗?
  回复  引用  查看    
#5楼 [楼主]2008-06-26 13:42 | Jian Sun      
排列组合的我写过一个, 等整理一下发上来吧
  回复  引用    
#6楼 2008-06-26 14:25 | 我是打酱油的 [未注册用户]
楼主的酱油不错,买了
  回复  引用    
#7楼 2008-06-26 15:57 | 卡巴司机 [未注册用户]
细细看来
  回复  引用  查看    
#8楼 2008-06-26 16:39 | 笨小孩儿      
耶?有意思,我还是第一次知道这种排序法,好玩好玩~~受教了~~
那为什么不直接用位运算一位一位地排呢?只有0 1两种选择岂不是更简单咯?位运算效率又高~
  回复  引用    
#9楼 2008-06-26 22:25 | sun jian [未注册用户]
回8楼, 因为计数排序是非比较的, 所以它的排序速度, 应该是在它的位容量越大越好, 这样才能发挥计数排序的优势, 充分利用空间换时间.

比如, 1-9容量的排序速度,显然要远远好于0-1的容量, 也显然远远差于0-99容量的排序速度
  回复  引用  查看    
#10楼 2008-06-27 03:38 | Sumtec      
时间复杂度计算错误!
该排序的时间复杂度是:
Os(n) = D*Od(n)
其中n代表数据的数量,Od(n)则是对n个元素的某一特定单位(例如一个十进制位)的排序时间复杂度,D表示按照特定单位分割后,该标准元素可分割出来的数量。例如楼主中的例子,D=3,至于Od(n)是多少,就不知道,估计最快也就只能是接近O(n)。

因此,除非讨论的是超大数排序,并且一次对超大数的排序速度,比D次对某一小单位数排序的速度还要慢,这种排序才有价值。例如某种机器对64位数进行任何计算所消耗的时间,都是对32位数计算所消耗时间的4倍,那么这种排序才有可能有价值。

  回复  引用  查看    
#11楼 [楼主]2008-06-27 10:02 | Jian Sun      
LS说的很对, 开头也说过了这个O(n)可能会带有一个很大的常数系数, 不一定比快速排序好多少.这个常数系数实际上就是有关数位的一个多项式.对于不太大的常数系数来说, 我觉得可以统略的称作O(n), 就像我们一般认为O(11n)是O(n)一样.

基数排序相对于计数排序, 是在空间和时间上取了折中, 即不需要太大的中间数组来存储中间排序结果(基数排序中计数排序子程序).而速度上也同样就引入了数位digit这个常量.当这个常量很大时, 基数排序就比计数排序要节省很多的空间, 同样也就越来越远离O(n)的时间复杂度.



标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: