排序算法总结:一、基数排序

基数排序(Radix sort)是一种非比较型的整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。
基数排序也分为LSD(Least significant digital)和MSD(Most significant digital)两种方式,LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

以LSD为例,假设原来有一串数值如下所示:
  73, 22, 93, 43, 55, 14, 28, 65, 39, 81

  首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
  0->NULL
  1->81->NULL
  2->22->NULL
  3->73->93->43->NULL
  4->14->NULL
  5->55->65->NULL
  6->NULL
  7->NULL
  8->28->NULL
  9->39->NULL

  接下来将这些桶中的数值重新串接起来,成为以下的数列:
  81, 22, 73, 93, 43, 14, 55, 65, 28, 39

  接着再进行一次分配,这次是根据十位数来分配:
  0->NULL
  1->14->NULL
  2->22->28->NULL
  3->39->NULL
  4->43->NULL
  5->55->NULL
  6->65->NULL
  7->73->NULL
  8->81->NULL
  9->93->NULL

  接下来将这些桶中的数值重新串接起来,成为以下的数列:
  14, 22, 28, 39, 43, 55, 65, 73, 81, 93

  这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。

可以看到,我们使用了单链表来实现向桶中添加数组元素的功能,下面是具体的c语言实现:

单链表头文件定义:

 1 #ifndef _List_H
 2 #define _List_H
 3
 4 struct Node;
 5 typedef int ElementType;
 6 typedef struct Node* PtrToNode;
 7 typedef PtrToNode List;
 8 typedef PtrToNode Position;
 9
10 List MakeEmpty(List L);
11 int IsEmpty(List L);
12 int IsLast(Position P, List L);
13 Position Find(ElementType X, List L);
14 Position FindLast(List L);
15 void Delete(ElementType X, List L);
16 Position FindPrevious(ElementType X, List L);
17 void Insert(ElementType X, List L, Position P);
18 void Append(ElementType X, List L);
19 void DeleteList(List L);
20 Position Header(List L);
21 Position First(List L);
22 Position Advance(Position P);
23 ElementType Retrieve(Position P);
24
25 #endif
26
27 struct Node
28 {
29     ElementType Element;
30     Position Next;
31 };

单链表的实现:

  1 #include "List.h"
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4
  5 void FatalError(char* c);
  6
  7 //测试链表是否为空(只有一个头指针)
  8 //时间复杂度为O(1)
  9 int IsEmpty(List L)
 10 {
 11     return L->Next == NULL;
 12 }
 13
 14 //测试当前位置是否是链表的末尾
 15 //时间复杂度为O(1)
 16 int IsLast(Position P, List L)
 17 {
 18     return P->Next == NULL;
 19 }
 20
 21 //寻找链表L中节点值为X的位置
 22 //时间复杂度为O(N)
 23 Position Find(ElementType X, List L)
 24 {
 25     Position P;
 26
 27     P = L->Next;
 28     while(P != NULL && P->Element != X)
 29     {
 30     P = P->Next;
 31     }
 32
 33     return P;
 34 }
 35
 36 //寻找并返回节点值为X的前置节点的位置
 37 //时间复杂度为O(N)
 38 Position FindPrevious(ElementType X, List L)
 39 {
 40     Position P;
 41
 42     P = L;
 43     while(P->Next != NULL && P->Next->Element != X)
 44     {
 45     P = P->Next;
 46     }
 47     return P;
 48 }
 49
 50 //寻找链表中最后一个节点的位置
 51 //时间复杂度为O(N)
 52 Position FindLast(List L)
 53 {
 54     Position P;
 55
 56     P = L;
 57     while(P->Next != NULL)
 58     {
 59     P = P->Next;
 60     }
 61     return P;
 62 }
 63
 64 //在链表L中删除节点值为X的节点
 65 //时间复杂度为O(N)
 66 void Delete(ElementType X, List L)
 67 {
 68     Position P, TmpCell;
 69
 70     P = FindPrevious(X, L);
 71
 72     if(!IsLast(P, L))
 73     {
 74     TmpCell = P->Next;
 75     P->Next = TmpCell->Next;
 76     free(TmpCell);
 77     }
 78 }
 79
 80 //在单链表L中,将节点X插入到位置P的后面
 81 //时间复杂度为O(1)
 82 void Insert(ElementType X, List L, Position P)
 83 {
 84     Position TmpCell;
 85
 86     TmpCell = malloc(sizeof(struct Node));
 87     if(TmpCell == NULL)
 88     {
 89     FatalError("Out of space!!");
 90     return;
 91     }
 92
 93     TmpCell->Element = X;
 94     TmpCell->Next = P->Next;
 95     P->Next = TmpCell;
 96 }
 97
 98 //将节点插入到链表最后面的位置
 99 //时间复杂度为O(N)
100 void Append(ElementType X, List L)
101 {
102     Insert(X, L, FindLast(L));
103 }
104
105 //删除全部链表,只留下头节点。
106 //时间复杂度为O(N)
107 void DeleteList(List L)
108 {
109     Position P, temp;
110
111     P = L->Next;
112     L->Next = NULL;
113
114     while(P != NULL)
115     {
116     temp = P->Next;
117     free(P);
118     P = temp;
119     }
120 }
121
122 void FatalError(char* c)
123 {
124     printf("Fatal Error: %s\n", c);
125 }

基数排序算法的实现:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include "List.h"
 4
 5 #define sizeOfArray 10
 6
 7 int* RadixSort(int*, int, int);
 8 void PrintAll(int*, int );
 9 void RebuildListHeaderArray(PtrToNode*, int );
10
11 int main(void)
12 {
13     int inputData[] = {73, 22, 393, 43, 255, 14, 28, 65, 39, 181};
14     int *newInputData;
15
16     printf("Initial:\n");
17     PrintAll(inputData, sizeOfArray);
18
19     newInputData = RadixSort(inputData, sizeOfArray, 3);
20
21     printf("Final:\n");
22     PrintAll(newInputData, sizeOfArray);
23
24     return 1;
25 }
26
27 //基数排序的时间复杂度是 O(k·n),其中n是排序元素个数,k是数字位数。
28 int* RadixSort(int inputData[], int arrayLength, int digitLength)
29 {
30     int i, j, k, div;
31     PtrToNode headerArray[arrayLength];
32
33     //Initialize
34     for(i=0; i<arrayLength; i++)
35     {
36     //Make a header first
37     PtrToNode header = malloc(sizeof(struct Node));
38     header->Element = i;
39     header->Next = NULL;
40
41     headerArray[i] = header;
42     }
43
44     //Insert List
45     for(i=0, div=1; i<digitLength; i++, div=div*10)
46     {
47     for(j=0; j<arrayLength; j++)
48     {
49         //Fetch a header first
50         PtrToNode header = headerArray[inputData[j] / div % 10];
51
52         //Insert into list
53         Append(inputData[j], header);
54     }
55
56     //Regenerate array
57     int index = 0;
58     for(k=0; k<arrayLength; k++)
59     {
60         Position p = headerArray[k];
61         while(p->Next != NULL)
62         {
63         p = p->Next;
64         inputData[index] = p->Element;
65         index++;
66         }
67     }
68     printf("The %d pass result:\n", i+1);
69     PrintAll(inputData, 10);
70     RebuildListHeaderArray(headerArray, 10);
71     }
72     return inputData;
73 }
74
75 void PrintAll(int *p, int arrayLength)
76 {
77     int i;
78     for(i=0; i<arrayLength; i++)
79     {
80     printf("%d, ", *(p+i));
81     }
82     printf("\n");
83 }
84
85 void RebuildListHeaderArray(PtrToNode headerArray[], int arrayLength)
86 {
87     int i;
88     for(i=0; i<arrayLength; i++)
89     {
90     DeleteList(headerArray[i]);
91     }
92 }

输出结果如下:
Initial:
73, 22, 393, 43, 255, 14, 28, 65, 39, 181,

The 1 pass result:
(按个位数排序)
181, 22, 73, 393, 43, 14, 255, 65, 28, 39,

The 2 pass result:
(按十位数排序)
14, 22, 28, 39, 43, 255, 65, 73, 181, 393,

The 3 pass result:
(按百位数排序)
14, 22, 28, 39, 43, 65, 73, 181, 255, 393,

Final:
14, 22, 28, 39, 43, 65, 73, 181, 255, 393,

可以看到在经过三趟排序后,算法结束。时间复杂度是O(digitLength*arrayLength), 也就是算法所用时间与输入元素中的最高位数与输入的元素数的乘积成正比。

时间: 2024-11-10 10:55:28

排序算法总结:一、基数排序的相关文章

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

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

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

与前面介绍的7种排序算法不同,基数排序(Radix Sort)是基于多关键字排序的一种排序算法.也就是说,前面介绍的7种排序算法是建立在对单个元素关键字比较的基础之上,而基数排序则是采用"分配"与"收集"的办法,用对多关键字进行排序的思想实现对单个关键字的排序. 基数排序的典型例子当然就是扑克牌排序啦,几乎所有的数据结构教科书都会讲到,原因是形象易懂.每张扑克牌都有两个关键字:花色和面值.假定有序关系为: 花色: 黑桃 < 红桃 < 梅花 < 方块

排序算法大全之基数排序

排序算法大全之--基数排序 基数排序是一种分配式排序,又成为桶子法排序 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 接下来,

排序算法六(基数排序)

一.原理介绍 所谓的基数排序算法,即使对于待排序数据,将其看做数据数位相同的数据. 随后一次依据每个数字个位大小排序,重新排序之后,再根据每一个数字的十位大小排序,依次进行,最后数据就可以达到一个整体有序的状态. 二.实例 对于这样的一系列数据: int arr[15] = { 123, 234, 543, 324, 568, 975, 547, 672, 783, 239,124,567,865,579,532 }; 我们依次检索个位,统计每一个数据的个位数,依次放置在 0-9 九个格子里,代

排序算法之JAVA基数排序算法

package net.qh.test.sort; import java.util.ArrayList; import java.util.List; /** * Created by Administrator on 2016/03/07. */ public class Radix { public static int[] asc( int[] array ){ int index = 1; /** 最大数字位数(如100为3位数字,2000为4位数字) */ /** 取得最大数字位数

【数据结构】非比较排序算法(实现计数排序和基数排序)

● 计数排序 1.算法思想: 计数排序是直接定址法的变形.通过开辟一定大小的空间,统计相同数据出现的次数,然后回写到原序列中. 2.步骤: 1)找到序列中的最大和最小数据,确定开辟的空间大小. 2)开辟空间,利用开辟的空间存放各数据的个数. 3)将排好序的序列回写到原序列中. 具体实现如下: void CountSort(int *arr, int size) {  assert(arr);  int min = arr[0];  int max = arr[0];  int num = 0;

常见的排序算法

描述: 排序算法可谓数据结构模块中的重中之重,常见的哈希表,二叉树,搜索树/平衡树,位图等数据结构只是处理实际问题的抽象方法,实际在处理接受或生成的数据集时,排序算法显得尤其重要,排序算法家族很庞大,其中包括了冒泡排序,选择排序,插入排序,堆排序,快速排序,归并排序,基数排序,计数排序,希尔排序,箱排序,树型排序等众多算法,每种排序都有各自的特性,没有好坏之分,只有在特定的场景使用合适的排序算法才是上策,单纯的来比显得太过绝对,没有可比性.因为实际需求及各方面条件的限制使得排序算法的可选范围往往

排序算法汇总(C/C++实现)

前言:     本人自接触算法近2年以来,在不断学习中越多地发觉各种算法中的美妙.之所以在这方面过多的投入,主要还是基于自身对高级程序设计的热爱,对数学的沉迷.回想一下,先后也曾参加过ACM大大小小的校级赛.区域赛.没什么惊天动地的奖项,最好的名次也就省三等奖.现在作为第一篇算法总结就拿常见的排序算法以我个人的理解,以及代码实现跟大家简单分享一下(排序算法如果一一罗列的话,不下十种.曾在图书馆的旧书架上看过一本近900页的书,内容就是专门介绍排序算法). 选择排序(select) 选择排序比较容

转:各种排序算法的稳定性和时间复杂度小结

选择排序.快速排序.希尔排序.堆排序不是稳定的排序算法, 冒泡排序.插入排序.归并排序和基数排序是稳定的排序算法. 冒泡法:  这是最原始,也是众所周知的最慢的算法了.他的名字的由来因为它的工作看来象是冒泡:  复杂度为O(n*n).当数据为正序,将不会有交换.复杂度为O(0). 直接插入排序:O(n*n) 选择排序:O(n*n) 快速排序:平均时间复杂度log2(n)*n,所有内部排序方法中最高好的,大多数情况下总是最好的. 归并排序:log2(n)*n 堆排序:log2(n)*n 希尔排序:

排序算法的稳定性

首先,排序算法的稳定性大家应该都知道,通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同.在简单形式化一下,如果Ai = Aj,Ai原来在位置前,排序后Ai还是要在Aj位置前. 其次,说一下稳定性的好处.排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用.基数排序就是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的.另外,如果排序算法稳定,对基于比较的排序算法而言,