常见排序算法导读(10)[基数排序]

与前面介绍的7种排序算法不同,基数排序(Radix Sort)是基于多关键字排序的一种排序算法。也就是说,前面介绍的7种排序算法是建立在对单个元素关键字比较的基础之上,而基数排序则是采用"分配"与"收集"的办法,用对多关键字进行排序的思想实现对单个关键字的排序。

基数排序的典型例子当然就是扑克牌排序啦,几乎所有的数据结构教科书都会讲到,原因是形象易懂。每张扑克牌都有两个关键字:花色和面值。假定有序关系为:

  • 花色: 黑桃 < 红桃 < 梅花 < 方块
  • 面值: 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < J < Q < K < A

如果把所有扑克牌排成下列次序:

  • 黑桃2, ..., 黑桃A, 红桃2, ..., 红桃A, 梅花2, ..., 梅花A, 方块2, ..., 方块A。

那么这就是多关键字排序。排序后形成的有序序列叫做词典有序序列。 对于扑克牌排序,有两种方法。可以先按花色排序,之后再按面值排序;也可以先按面值排序,再按花色排序。

再举个对数字进行基数排序的例子,

基数排序的基本思想

设待排记录中的多个关键字为K1, K2, ..., Kr。K1第1关键字,Kr为第r关键字。由记录R1, R2, ..., Rn组成的表关于关键字K1, K2, ..., Kr有序,当且仅当对每一对记录Ri < Rj 都有(K1i, K2i, ..., Kri) <= (K1j, K2j, ..., Krj)。

基数排序的分类

  1. 最高位优先MSD(Most Significant Digit first)
  2. 最低位优先LSD(Least Significant Digit fisrt)

基于LSD实现的基数排序相对简单。(P.S. 在观看了Radix Sort Visualization之后,我大约花了15分钟就完成了代码实现,比啃数据结构的书容易多了。)

  1 /*
  2  * Get the digit of number by index(= 0, 1, 2, ...)
  3  *
  4  * e.g.
  5  *     num = 6543210
  6  *     ------+------
  7  *     index | digit
  8  *     ------+------
  9  *         0 | 0
 10  *         1 | 1
 11  *         2 | 2
 12  *         3 | 3
 13  *         4 | 4
 14  *         5 | 5
 15  *         6 | 6
 16  *         7 | 0
 17  *        .. | ..
 18  *     ------+------
 19  */
 20 unsigned char
 21 get_digit_byindex(int num, unsigned int index)
 22 {
 23         int q = 0; /* quotient */
 24         int r = 0; /* remainder */
 25         for (int i = index; i >= 0; i--) {
 26                 r = num % 10;
 27                 q = num / 10;
 28                 num = q;
 29         }
 30
 31         return (unsigned char)r;
 32 }
 33
 34 /*
 35  * Get width of a number
 36  * e.g.
 37  *     0 .. 9   ; width = 1
 38  *    10 .. 99  ; width = 2
 39  *   100 .. 999 ; width = 3
 40  *   ... .. ...
 41  */
 42 static int
 43 get_width_of_num(int num)
 44 {
 45         if (num < 0)
 46                 num = 0 - num;
 47
 48         int w = 1;
 49         for (int q = num / 10; q != 0; q /= 10)
 50                 w++;
 51         return w;
 52 }
 53
 54 /*
 55  * Build bin[] by index(=0, 1, ..., N-1) (N is the max width of num in a[])
 56  *
 57  * NOTE: This is the core to understand radix sorting based on LSD
 58  */
 59 static void
 60 build_bin_byindex(int bin[], size_t nb, int a[], size_t na, int index)
 61 {
 62         /* 1. always reset bin[] */
 63         for (int i = 0; i < nb; i++)
 64                 bin[i] = 0;
 65
 66         /* 2. init bin[] by walking a[] */
 67         for (int i = 0; i < na; i++) {
 68                 unsigned char d = get_digit_byindex(a[i], index);
 69                 bin[d] += 1;
 70         }
 71
 72         /* 3. build bin[] */
 73         for (int i = 1; i < nb; i++)
 74                 bin[i] += bin[i-1];
 75 }
 76
 77 void
 78 radixsort(int a[], int n)
 79 {
 80         /* get the max width of num in a[] */
 81         int maxwidth = 0;
 82         for (int i = 0; i < n; i++) {
 83                 int width = get_width_of_num(a[i]);
 84                 if (width > maxwidth)
 85                         maxwidth = width;
 86         }
 87
 88         /* alloc bin[] to store the number of per digit */
 89         int bin[10] = { 0 };
 90
 91         /* alloc aux[] to save a[] while rebuilding a[] */
 92         int *aux = (int *)malloc(sizeof(int) * n);
 93
 94         /* LSD (Least Significant Digit first) */
 95         for (int index = 0; index < maxwidth; index++) {
 96                 /* 1. build bin[] */
 97                 build_bin_byindex(bin, 10, a, n, index);
 98
 99                 /* 2. copy a[] to aux[] */
100                 for (int i = 0; i < n; i++)
101                         aux[i] = a[i];
102
103                 /* 3. reset a[] */
104                 for (int i = 0; i < n; i++)
105                         a[i] = 0;
106
107                 /* 4. rebuild a[] */
108                 for (int i = n - 1; i >= 0; i--) {
109                         unsigned char d = get_digit_byindex(aux[i], index);
110                         int j = bin[d] - 1;
111                         a[j] = aux[i];
112                         bin[d]--;
113                 }
114         }
115
116         free(aux);
117 }

参考资料:

  1. Radix Sort Visualization
  2. Radix sort
时间: 2024-09-30 20:39:40

常见排序算法导读(10)[基数排序]的相关文章

常见排序算法导读(3)[简单选择排序]

这一节将介绍简单选择排序(Simple Selection Sort). 在介绍简单排序算法之前,先给出排序的确切定义,并简单介绍一下排序算法的稳定性. 排序的确切定义 假设含有n个对象的序列为{R[0], R[1], ..., R[n-1]}, 其对应的关键字(key)序列为{K[0], K[1], ..., K[n-1]}. 所谓排序, 就是确定0, 1, ..., n-1的一种排列p[0], p[1], ..., p[n-1], 使各个关键字满足如下的非递减(升序)或非递增(降序)关系:

常见排序算法导读(11)[桶排序]

上一节讲了基数排序(Radix Sort),这一节介绍桶排序(Bucket Sort or Bin Sort).和基数排序一样,桶排序也是一种分布式排序. 桶排序(Bucket Sort)的基本思想 将待排对象序列按照一定hash算法分发到N个桶中 对每一个桶的待排对象进行排序 从头到尾遍历N个桶,收集所有非空的桶里的有序对象(子序列)组成一个统一的有序对象序列 在每一个桶中,如果采用链式存储的话,1.和2.可以合并在一起操作,也就是在分发的过程中保证每一个桶的对象桶内有序. 例如: 设有5个桶

十种常见排序算法

1.常见算法分类 十种常见排序算法一般分为以下几种: (1)非线性时间比较类排序:交换类排序(快速排序和冒泡排序).插入类排序(简单插入排序和希尔排序).选择类排序(简单选择排序和堆排序).归并排序(二路归并排序和多路归并排序): (2)线性时间非比较类排序:计数排序.基数排序和桶排序. 总结: (1)在比较类排序中,归并排序号称最快,其次是快速排序和堆排序,两者不相伯仲,但是有一点需要注意,数据初始排序状态对堆排序不会产生太大的影响,而快速排序却恰恰相反. (2)线性时间非比较类排序一般要优于

常见排序算法(一) MergeSort

算法思想灰常重要,常见的用到分治思想的算法包括快速排序,归并,二分搜搜,大整数乘法等(参考 http://blog.csdn.net/com_stu_zhang/article/details/7233761,归纳很到位) 简单用归并对一个数组排序 思路: 简单来说对一个数组,只要他的左右两部分都是有序的,那么简单合并就ok了,那么左右两部分可以进一步划分各自的左右两部分----明显就是要递归了 算法:归并排序 1. 将数组一分为二,subArray1 和subArray2 2. 归并排序sub

常见排序算法(java实现)

常见排序算法介绍 冒泡排序 代码: public class BubbleSort { public static void sort(int[] array) { int tValue; for (int i = 0; i < array.length; i++) { for (int j = i; j < array.length; j++) { if (array[i] > array[j]) { tValue = array[i]; array[i] = array[j]; ar

排序算法七:基数排序(Radix sort)

上一篇提到了计数排序,它在输入序列元素的取值范围较小时,表现不俗.但是,现实生活中不总是满足这个条件,比如最大整形数据可以达到231-1,这样就存在2个问题: 1)因为m的值很大,不再满足m=O(n),计数排序的时间复杂也就不再是线性的: 2)当m很大时,为计数数组申请的内存空间会很大: 为解决这两个问题,本篇讨论基数排序(Radix sort),基数排列的思想是: 1)将先按照某个基数将输入序列的每个元素划分成若干部分,每个部分对排序结果的影响是有优先级的: 2)先按低优先级排序,再按高优先级

常见排序算法(冒泡、选择、插入、快速、归并C++实现)

常见排序算法(冒泡.选择.插入.快速.归并C++实现) #include <iostream> using namespace std; // 冒泡排序 void bubbleSort (int data[], size_t size) { for (size_t i = 0; i < size - 1; ++i) { bool ordered = true; for (size_t j = 0; j < size - 1 - i; ++j) if (data[j+1] <

第六章 常见排序算法

上章回顾 二叉树的定义 树深度的定义 什么样的二叉树是满二叉树 中序遍历的规则 [email protected]:Kevin-Dfg/[email protected]:Kevin-Dfg/Data-Structures-and-Algorithm-Analysis-in-C.git 第六章 第六章 常见排序算法 常见排序算法 [email protected]:Kevin-Dfg/[email protected]:Kevin-Dfg/Data-Structures-and-Algorith

排序算法大全之基数排序

排序算法大全之--基数排序 基数排序是一种分配式排序,又成为桶子法排序 LSD(我们以最低位优先) 第一步:假设原有一串数字如下: 23,45,12,32,43 遍历这些数的个位数字,将他们分别装进编号为0到9的桶中 桶  0:为空,因为这些数中没有个位数为0的 桶  1:空 桶  2:12,32 桶  3:23,43 桶  4:空 桶  5:45 桶  6:空 桶  7:空 桶  8:空 桶  9:空 第二步 接下来将这些桶中的数据依次串起来,排成一排: 12,32,23,43,45 接下来,