排序算法总结:一、基数排序
2015-07-15 17:25
537 查看
基数排序(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语言实现:
单链表头文件定义:
单链表的实现:
基数排序算法的实现:
输出结果如下:
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), 也就是算法所用时间与输入元素中的最高位数与输入的元素数的乘积成正比。
基数排序也分为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语言实现:
单链表头文件定义:
#ifndef _List_H #define _List_H struct Node; typedef int ElementType; typedef struct Node* PtrToNode; typedef PtrToNode List; typedef PtrToNode Position; List MakeEmpty(List L); int IsEmpty(List L); int IsLast(Position P, List L); Position Find(ElementType X, List L); Position FindLast(List L); void Delete(ElementType X, List L); Position FindPrevious(ElementType X, List L); void Insert(ElementType X, List L, Position P); void Append(ElementType X, List L); void DeleteList(List L); Position Header(List L); Position First(List L); Position Advance(Position P); ElementType Retrieve(Position P); #endif struct Node { ElementType Element; Position Next; };
单链表的实现:
#include "List.h" #include <stdio.h> #include <stdlib.h> void FatalError(char* c); //测试链表是否为空(只有一个头指针) //时间复杂度为O(1) int IsEmpty(List L) { return L->Next == NULL; } //测试当前位置是否是链表的末尾 //时间复杂度为O(1) int IsLast(Position P, List L) { return P->Next == NULL; } //寻找链表L中节点值为X的位置 //时间复杂度为O(N) Position Find(ElementType X, List L) { Position P; P = L->Next; while(P != NULL && P->Element != X) { P = P->Next; } return P; } //寻找并返回节点值为X的前置节点的位置 //时间复杂度为O(N) Position FindPrevious(ElementType X, List L) { Position P; P = L; while(P->Next != NULL && P->Next->Element != X) { P = P->Next; } return P; } //寻找链表中最后一个节点的位置 //时间复杂度为O(N) Position FindLast(List L) { Position P; P = L; while(P->Next != NULL) { P = P->Next; } return P; } //在链表L中删除节点值为X的节点 //时间复杂度为O(N) void Delete(ElementType X, List L) { Position P, TmpCell; P = FindPrevious(X, L); if(!IsLast(P, L)) { TmpCell = P->Next; P->Next = TmpCell->Next; free(TmpCell); } } //在单链表L中,将节点X插入到位置P的后面 //时间复杂度为O(1) void Insert(ElementType X, List L, Position P) { Position TmpCell; TmpCell = malloc(sizeof(struct Node)); if(TmpCell == NULL) { FatalError("Out of space!!"); return; } TmpCell->Element = X; TmpCell->Next = P->Next; P->Next = TmpCell; } //将节点插入到链表最后面的位置 //时间复杂度为O(N) void Append(ElementType X, List L) { Insert(X, L, FindLast(L)); } //删除全部链表,只留下头节点。 //时间复杂度为O(N) void DeleteList(List L) { Position P, temp; P = L->Next; L->Next = NULL; while(P != NULL) { temp = P->Next; free(P); P = temp; } } void FatalError(char* c) { printf("Fatal Error: %s\n", c); }
基数排序算法的实现:
#include <stdio.h> #include <stdlib.h> #include "List.h" #define sizeOfArray 10 int* RadixSort(int*, int, int); void PrintAll(int*, int ); void RebuildListHeaderArray(PtrToNode*, int ); int main(void) { int inputData[] = {73, 22, 393, 43, 255, 14, 28, 65, 39, 181}; int *newInputData; printf("Initial:\n"); PrintAll(inputData, sizeOfArray); newInputData = RadixSort(inputData, sizeOfArray, 3); printf("Final:\n"); PrintAll(newInputData, sizeOfArray); return 1; } //基数排序的时间复杂度是 O(k·n),其中n是排序元素个数,k是数字位数。 int* RadixSort(int inputData[], int arrayLength, int digitLength) { int i, j, k, div; PtrToNode headerArray[arrayLength]; //Initialize for(i=0; i<arrayLength; i++) { //Make a header first PtrToNode header = malloc(sizeof(struct Node)); header->Element = i; header->Next = NULL; headerArray[i] = header; } //Insert List for(i=0, div=1; i<digitLength; i++, div=div*10) { for(j=0; j<arrayLength; j++) { //Fetch a header first PtrToNode header = headerArray[inputData[j] / div % 10]; //Insert into list Append(inputData[j], header); } //Regenerate array int index = 0; for(k=0; k<arrayLength; k++) { Position p = headerArray[k]; while(p->Next != NULL) { p = p->Next; inputData[index] = p->Element; index++; } } printf("The %d pass result:\n", i+1); PrintAll(inputData, 10); RebuildListHeaderArray(headerArray, 10); } return inputData; } void PrintAll(int *p, int arrayLength) { int i; for(i=0; i<arrayLength; i++) { printf("%d, ", *(p+i)); } printf("\n"); } void RebuildListHeaderArray(PtrToNode headerArray[], int arrayLength) { int i; for(i=0; i<arrayLength; i++) { DeleteList(headerArray[i]); } }
输出结果如下:
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), 也就是算法所用时间与输入元素中的最高位数与输入的元素数的乘积成正比。
相关文章推荐
- IOS AppDelegate方法全解
- android二维码扫描
- vs2008+matlab2010b--matlab转换为dll的操作步骤 ( 一)
- 深度剖析HTML的语意和与其相关的前端框架
- 源码推荐(7.15):重写UIFont方法 设置字体格式以及大小,可重用的水平滚动scrollView列表 极致性能优化
- 一个普通骰子,连抛3次都是1点,请问抛第4次的概率是多少?
- android-整体UI设计-(滑动导航栏+滚动页面)
- [Elasticsearch] 控制相关度 (六) - function_score查询中的filter,functions及random_score参数
- OpenJWeb3.1 Java Web应用快速开发平台全部开源公告
- winform计算器
- 九度oj 1010
- Android ViewPager 修改滑动速度
- JQuery的animate
- 怎样使用相对路径来放置js文件
- Block块的学习笔记
- android进程生命周期
- [Elasticsearch] 控制相关度 (五) - function_score查询及field_value_factor,boost_mode,max_mode参数
- android 常见分辨率(mdpi、hdpi 、xhdpi、xxhdpi )及屏幕适配注意事项
- 总结CSS3新特性(选择器篇)
- mysql 定时任务