[算法]基数排序简述

  在学习了计数排序后,可以发现一个很严重的问题,如果数据很大呢,不如说每个元素小于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)的时候排序,比如说年月日等等,

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

时间: 2024-08-29 13:06:21

[算法]基数排序简述的相关文章

经典排序算法 - 基数排序Radix sort

经典排序算法 - 基数排序Radix sort 原理类似桶排序,这里总是需要10个桶,多次使用 首先以个位数的值进行装桶,即个位数为1则放入1号桶,为9则放入9号桶,暂时忽视十位数 例如 待排序数组[62,14,59,88,16]简单点五个数字 分配10个桶,桶编号为0-9,以个位数数字为桶编号依次入桶,变成下边这样 |  0  |  0  | 62 |  0  | 14 |  0  | 16 |  0  |  88 | 59 | |  0  |  1  |  2  |  3  |  4 | 

排序算法----基数排序(RadixSort(L))单链表智能版本

转载http://blog.csdn.net/Shayabean_/article/details/44885917博客 先说说基数排序的思想: 基数排序是非比较型的排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较. 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零.然后,从最低位开始,依次进行一次排序.在每一次排序中,按照当前位把数组元素放到对应 的桶当中,然后把桶0到桶9中的元素按先进先出的方式放回数组中.这样从最低位排序一直到最高位排序完成以后,

排序算法----基数排序(RadixSort(L,max))单链表版本

转载http://blog.csdn.net/Shayabean_/article/details/44885917博客 先说说基数排序的思想: 基数排序是非比较型的排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较. 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零.然后,从最低位开始,依次进行一次排序.在每一次排序中,按照当前位把数组元素放到对应 的桶当中,然后把桶0到桶9中的元素按先进先出的方式放回数组中.这样从最低位排序一直到最高位排序完成以后,

算法-基数排序(radix sort)

本文由@呆代待殆原创,转载请注明出处. 简介:这个排序是原来用在卡片排序机上的一个算法,一般用来比较具有多对关键字域的记录,如日期(年月日),通过基数排序我们会依次对年月日这三个关键字进行排序,只要对每个关键字进行排序的算法是稳定的,那么最后输出的序列就一定是正确的. 思路:基数排序思路很简单,首先取第一个关键字,然后对其进行排序,在第一次排序的基础上取第二个关键字,再对其进行排序,直到遍历完所有的关键字,一般用计数排序实现基数排序. 算法分析 时间复杂度:Θ(i*x)  i 是关键码的数量,x

贪心算法的简述与示例

贪心算法采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯.能够用贪心算法求解的问题一般具有两个重要特性:贪心选择性质和最优子结构性质. 参考:http://babybandf.blog.163.com/blog/static/61993532010112923767/ [例1]删数问题[B][/B] 试题描

Prime 算法的简述

前面在介绍并查集时顺便提了Kruskal算法,既然已经说到了最小生成树问题,就没有道理不把Prime算法说了. 这里面先补充下Kruskal算法的大概意思,Kruskal算法通过把所有的边从小到大排列后,不断取权值最小的边加入最小生成树(起初可能是离散的多个树,最终连成一个整体),并通过并查集来舍弃形成回路的边. Prime算法有所不同,Prime算法先将一个起点加入最小生成树,之后不断寻找与最小生成树相连的边权最小的边能通向的点,并将其加入最小生成树,是一种更符合人的主观直觉的最小生成树算法.

排序算法——基数排序(桶式排序)

基数排序(radix sort)属于"分配式排序"(distribution sort),又称"桶子法"(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些"桶"中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法.(引自百度百科) 简单的理解就是按权高低依次排序.比如说

最长上升子序列问题 nlogn 实现算法的简述

首先举个例子说明最长上升子序列(longest increasing subsequence 缩写 LIS): 1,4,6,2,3,7,5 中1,2,3,5 和1,4,6,7都是最长上升子序列,长度均为4,且相邻元素不能相等. LIS是动态规划中的经典问题,O(n2)的做法是设d(i)为以i为结尾的最长上升子序列的长度,状态转移方程为:d[i]=max{0,d[j]|j<i,A[j]<A[i]}+1. 下面我们仔细思考以下情况: i<j时,d[i]=d[j],显然这种情况只能是A[i]&

排序算法--基数排序

#define _CRT_SECURE_NO_WARNINGS #include<math.h> #include <stdio.h> #include <stdlib.h> int findMaxNum(int *p, int n); void sort2(int *p, int n, int loop); void bucketSort3(int *p, int n); int getLoopTimes(int num); /* 基数排序原理: 求出数组中最大值 求