码迷,mamicode.com
首页 > 编程语言 > 详细

[算法]基数排序简述

时间:2016-08-09 13:36:09      阅读:200      评论:0      收藏:0      [点我收藏+]

标签:

  在学习了计数排序后,可以发现一个很严重的问题,如果数据很大呢,不如说每个元素小于2^64 - 1,超时可能不怎么会,数据不好的

情况下会超内存

  (虽然可以直接用快排,但是为了讲解基数排序还是讲一下基数排序)

  基数排序可以说成是改良版的桶排,还是将一些数放入指定的桶中

比如一串数  12  33  43  54  65  39  45  72  89

首先按照个位分配到 0 - 10 这11个“桶”中

 0
 1 
 2 12  72
 3 33  43
 4 54
 5 65  45
 6
 7 
 8 
 9 39  89

接着再串起来,得到了下面这样的数列

12    72    33    43    54    65    45    39    89

再按照十位进行分配桶

 0
 1 12
 2 
 3 33  39
 4 43  45
 5 54
 6 65
 7 72
 8 89
 9 

同上,再把这些桶串起来

12    33    39    43    45    54    65    72    89   

这时候的数列已经是有序的了,不需要下一轮的分配了

这样不如说成从低位到高位,按位排序

可能可以想到,有没有可能出现从小到大排序出现这样的情况

  46  42

这样是不会出现的,稍微想一下就可以明白

  先按个位排序,46第一次排序完了一定是在42的后面

  再按十位排序,在4这个桶内46一定是42后面,因为是按顺序放在后面

上面这种方法又叫做LSD(最低位优先)法,先给出LSD的实现(中间用到了

计数排序,和上面有一点不同,但是仍然不难理解)

技术分享
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 int* counter;
 6 int* buf;
 7 int maxbit(int* array, int len){
 8     int result = 1;
 9     int p = 1;
10     for(int i = 0;i < len;i++){
11         while(((p << 3) + (p << 1)) <= array[i]){
12             p = (p << 3) + (p << 1);        //p *= 10; 
13             result++;
14         }
15     }
16     return result;
17 }
18 void radixSort(int* &array, int len){
19     buf = new int[(const int)(len + 1)];
20     counter = new int[11];
21     int limit = maxbit(array, len);
22     int p = 1;
23     for(int i = 0;i < limit;i++){
24         memset(counter, 0, sizeof(int) * 11);
25         for(int j = 0;j < len;j++)
26             counter[(array[j] / p) % 10]++;
27         for(int j = 1;j < 11;j++)
28             counter[j] += counter[j - 1];
29         for(int j = len - 1;j >= 0;j--)
30             buf[--counter[(array[j] / p) % 10]] = array[j];
31         swap(array, buf);
32         p = (p << 3) + (p << 1);
33     }
34     delete[] buf;
35     delete[] counter;
36 }
37 int n;
38 int *a;
39 int main(){
40     cin>>n;
41     a = new int[(const int)n];
42     for(int i = 0;i < n;i++)
43         cin>>a[i];
44     radixSort(a, n);
45     for(int i = 0;i < n;i++)
46         cout<<a[i]<<" ";
47     return 0;
48 } 
LSD

  如果从高位开始按位排序又叫做MSD(最高位优先)法

如果您按照上面那种方法去做,很快就可以发现在对于这样一组数据是不行的

  32  13  12

  MSD的做法除了顺序外还有不同之处,如果一个桶中的元素不止一个,就对这个桶中

的元素进行下一位的排序

  例如上面这组数据,首先第一轮分配

0
1 13  12
2 
3 32

1这个桶内的元素不止一个,递归调用进行第二轮分配,分配个位

0 
1 
2 12
3 13

发现所有的桶中的元素都没有超过1个,返回(另外还要拷贝并覆盖原来那

  如果像LSD那样用计数排序(省内存),貌似不太好处理,不过请仔细观察counter数组

在排序的过程中,counter对应位上值会减去一,排序完后这一位相当于前一位原来的值

  如果counter[i] != counter[i + 1]说明i这个桶中有东西,计算有多少个东西就直接

counter[i + 1] - counter[i]就能算出来了,起始位置就是array[counter[i]],这样就解决

另外还要注意,如果是最低位就不用再递归调用了(如果忘记这一点可能不会超时,应该很快就

会整数被0除)

  附上MSD的代码(也没改多少)

技术分享
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 int* buf;
 6 int p;
 7 int maxbit(int* array, int len){
 8     int result = 1;
 9     p = 1;
10     for(int i = 0;i < len;i++){
11         while(((p << 3) + (p << 1)) <= array[i]){
12             p = (p << 3) + (p << 1);        //p *= 10; 
13             result++;
14         }
15     }
16     return result;
17 }
18 /**
19  * 基数排序MSD法 
20  * @param array 数组的起始位置
21  * @param len 长度
22  * @param status 辅助作用 
23  */ 
24 void radixSort(int* array, int len, int status){
25     buf = new int[(const int)(len + 1)];
26     int* counter = new int[11];            //一定要定成局部变量 
27     memset(counter, 0, sizeof(int) * 11);
28     for(int j = 0;j < len;j++)
29         counter[(array[j] / status) % 10]++;
30     for(int j = 1;j < 10;j++)
31         counter[j] += counter[j - 1];
32     for(int j = len - 1;j >= 0;j--)
33         buf[--counter[(array[j] / status) % 10]] = array[j];
34     for(int i = 0;i < len;i++)
35         array[i] = buf[i];
36     delete[] buf;
37     for(int i = 0;i < 9;i++){                /* 这里开始 */ 
38         int l = counter[i];
39         int r = counter[i + 1] - 1;
40         if(l < r && status > 1){
41             radixSort(array + counter[i], r - l + 1, status / 10);
42         }
43     }                                        /* 这里结束 */ 
44     delete[] counter;
45 }
46 int n;
47 int *a;
48 int main(){
49     cin>>n;
50     a = new int[(const int)n];
51     for(int i = 0;i < n;i++)
52         cin>>a[i];
53     maxbit(a, n);
54     radixSort(a, n, p);
55     for(int i = 0;i < n;i++)
56         cout<<a[i]<<" ";
57     return 0;
58 } 
MSD

[后记]

  看完之后,仔细思考,应该可以发现一个很严重的问题——不能给负数排序

那怎么办呢?

那我就说说的几种方法

  1)如果数据的大小不超过int,就全部加上(unsigned)0xffffffff用long long存储

   输出的时候再减去(unsigned)0xffffffff

  2)将非负整数用一个数组存储,将负数用另外一个数组存储,负数并且全部取反(是数学上的-)

   先将前者排序,再将后者用同一个函数排序,输出的时候先输出后者,从高下标往低下标输出,

   并把负号添上,再输出前者,从低下标到高下标


  基数排序给整数排序的时候并不是用得特别多,更多的时候是用于比较复杂度不是O(1)的时候排序,比如说年月日等等,

后缀数组的构造(当然要用倍增)用基数排序也会比用快排快很多

 

[算法]基数排序简述

标签:

原文地址:http://www.cnblogs.com/yyf0309/p/5750609.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!