学习基数排序心得

学习基数排序心得

为什么要学

    昨天和同学讨论了到底是快速排序快,还是二路归并快.同学说是快排快,但我说是二路归并快(因为稳定).
    今天就把我所知的快排,二路归并和<algorithm>里的sort做了一个千万级随机数排序,结果是快排最快,sort比二路归并快...sad
    但是我依稀还记得有一个排序算法好像还没看,就是基数排序.
    先看一下效果图:

正整数的排序(debug)

怎么实现

如何学习基数排序呢?
    其实我学习基数排序也是各种百度,看代码,但是看了很长时间都没有什么收获,只知道一个原理(原理什么的,一百度就知道了),
    对于怎么实现简洁的代码没有头绪.
    当然现在我会了,所以我来分享一下学习心得.(^_^)

原理:将一个数组中的数按照个位,十位,百位...排序,最后得到的是有序的数组.
for example:
    如果a>b,那么a的某一位一定比b的那一位大
    123890>122890(万位)
    123>122(个位) 89>78(十位和个位)

那么基数排序就利用这一点先对一群数的个位排序,在对十位排序...直到这群数中的最大值的最高一位.

实现

void radixsort(int arr[],int length)

写成这种参数比较实用,当然还有很多方式.

int count[10];
int* temp = new int[length];

申请两个数组,一个是用于计数的count数组(因为操作的是十进制数,所以开的大小是10)
另一个是用于临时存放变量数组temp,和原数组一样大

int i,bitnum=1,ten=10;
for(i=0;i<length;i++)
    while(arr[i]/ten)
        ten*=10,bitnum++;

这一段定义了循环变量i,数组中最大值的位数bitnum,和用来存10的n次的变量ten

这段代码的意思是:如果能被10除了之后为零,那么这个数一定是个一位数,否则ten变成100….再如果这个数被100除了之后变成0,那么这个数就是个两位数,否则就一直循环.

ten=1;
while(bitnum--)
{
    memset(count,0,sizeof count);
    /*4个循环*/
    ten*=10;
}
delete []temp;

这一段开始正式处理arr数组

  • 首先我们知道,基数排序执行的次数是bitnum次,而且在循环里面也不会用到这个bitnum,所以直接while(bitnum–)
  • 其次每个循环都要初始化count数组为0
  • 再然后做完排序之后呢,要手动删除temp申请的内存,系统才不会给你释放(程序关掉之后当然会释放)
  • 变量ten是用于截取数的各个位(现在不知道不要紧,往下看)
    循环1:
for(i=0;i<length;i++)
    count[arr[i]/ten%10]++;

这么写非常的紧凑,但是他是合法的
首先遍历整个数组,对每一个数的一个位数,count对应指示器加一
for example:
    当ten=1时   arr[i]/i%10=arr[i]的个位
    123/1%10=123%10=3
    ten=100 : arr[i]/i%10=arr[i]的百位
    123/100%10=1%10=1
    那么我们就知道ten的作用:ten每次乘10都是想在下一个循环取到更高位的数字
    所以我们就把arr[i]/ten%10当做取arr[i]中的某一位
  取出来之后在对应count计数器上加一
  count[x]代表数组中某一位是x的整数数量

循环2:

for(i=1;i<10;i++)
    count[i]+=count[i-1];

这个循环是将count的意义从(数组中某一位是x的整数数量)变成了(数组中某一位小于等于x的整数数量)

循环3:

for(i=length-1;i>-1;i--)
    temp[--count[arr[i]/ten%10]]=arr[i];
///可分为:
//1.for(i=length-1;i>-1;i--)
//2.x=--count[ (arr[i]/ten%10) ];
//3.temp[x]=arr[i];

到了最难的地方
先解释一下count[x]的意思:某一位小于等于x的整数数量
for example:

  • 123 345 456 234 234 132 451
  • 个位的count[10]={0,1,2,3,5,6,7,7,7,7}
  • count[0]:数组中没有个位为0的数字
  • count[1]:数组中有1个 个位小于等于1的数字 451
  • count[2]:数组中有2个 个位小于等于1的数字 +132
  • count[3]:数组中有3个 个位小于等于1的数字 +123
  • count[4]:数组中有5个 个位小于等于1的数字 +234 +234
  • count[6]:数组中有6个 个位小于等于1的数字 +345
  • count[7]:数组中有7个 个位小于等于1的数字 +456
  • (+指的是加上之前的)
  • 那么我们想到345应该先排在哪里呢?
  • 应该排在count[6]-1处,因为它本应该排在第count[6]个,但是下标从0开始
  • 排好了之后呢数组中个位小于等于1的数字应该减少,所以count[6]要减一
  • 索性将这两步合起来写成 –count[x]
  • x=arr[i]/ten%10 这个式子的意思在之前解释过了

所以
第一步:遍历数组从后往前(为什么,知道的可以告诉我,我也不知道,但是不可以反过来)
第二步:找出arr[i]应该排在哪里 在下标为x=–count[arr[i]/ten%10]这个位置
第三步:把这个数记到temp[x]里

循环4:

for(i=0;i<length;i++)
    arr[i]=temp[i];

这个循环很简单,就是把temp数组再复制回arr数组里

四个循环下来,某一位就排好了

完整代码:

void radixsort(int* arr,int length)
{
    int count[10];
    int*temp = new int[length];
    int i,bitnum=1,ten=10;
    for(i=0;i<length;i++)
        while(arr[i]/ten)
            ten*=10,bitnum++;
    ten=1;
    while(bitnum--)
    {
        memset(count,0,sizeof count);
        for(i=0;i<length;i++)
            count[arr[i]/ten%10]++;
        for(i=1;i<10;i++)
            count[i]+=count[i-1];
        for(i=length-1;i>-1;i--)
            temp[--count[arr[i]/ten%10]]=arr[i];
        for(i=0;i<length;i++)
            arr[i]=temp[i];
        ten*=10;
    }
    delete []temp;
}

这段代码所实现的基数排序的弊端

  • 1.正负数要分开来排序
  • 2.只能处理整数,不能处理浮点数
  • 其他的我暂时看不到,欢迎补充

猜你喜欢

转载自blog.csdn.net/qq_30396205/article/details/70176478