基数排序和其他排序方法不同,实现基数排序不需要对元素之间进行比较,它主要是利用多个关键字进行排序。
1.桶排序
例如,年纪有1000个人,他们的学习成绩在0-100分之间,那么,对这1000个人排序我们只需要按照分数0,1,2,… ,99,100,建立101个桶,然后依次把每个学生的成绩放入桶内,这样就完成了排序。
算法分析:
- 首先我们知道学生的成绩范围,ScanForMax(SqList L),才知道我要建立多少个桶;
- 要依次读入每个学生的成绩,然后按照成绩放入桶内。
- 最后按照桶的顺序依次把所有成绩输出
void Budget_Sort(SqList *L)
{/*桶排序,每个关键字对应一个桶*/
int NumBudget;
typedef struct Node
{/*定义一个链表*/
ElementType data;
struct Node *next;
}List;
typedef struct
{/*桶*/
List *Li;//存桶中元素链表
int NumElement;//桶中元素的个数
}Budget;
/*首先应该知道序列的最大值是多少,才能确定分配多少个桶,每个数据放入一个桶,每个桶可能放多个相同的数据*/
NumBudget = ScanForMax(*L);
Budget *Budg;
Budg = (Budget*)malloc(sizeof(Budget)*(NumBudget+1));
if (!Budg)//分配失败
exit(-1);
for (int i = 0; i <= NumBudget; i++)
{/*初始化每个桶*/
Budg[i].NumElement = 0;
Budg[i].Li = NULL;
}
int i = 0;
while (i<L->Length)
{/*依次读入每个元素,插入到相应的桶的链表第一个位置*/
List *p;
p= (List*)malloc(sizeof(List));//为链表申请一个节点空间
if (!p)
exit(-1);
p->data = L->data[i];
p->next = Budg[L->data[i]].Li;
Budg[L->data[i]].Li = p;//桶的链表头变成p
Budg[L->data[i]].NumElement++;//桶中的元素数量加1
i++;
}
int j=0;
List *p;
for (int i = 0; i <=NumBudget; i++)
{/*将桶中的数据取出来按顺序放入原顺序表*/
p = Budg[i].Li;//链表头指针
while (p)
{
L->data[j++] = p->data;
p = p->next;
}
}
}
性能分析:算法复杂度为O(M+N),M为桶的个数,N为待排序元素个数。当N>>M时这种方法效率很高,但是当M>>N时,例如10个学生有成绩分布在0~1000分,这种方法就效率低下。
2. 基数排序(次位优先)
如果待排序元素是10进制数,那么我们就建立10个桶(0,1,2,… ,8,9);同理八进制就8八个桶。然后我们需要知道元素的最大值是多少位的,这样就排多少次顺序。例如如果有百位,我们就先按照个位pass一次,把元素根据个位值放在各个桶内,结在一次将元素从个位桶里取出来再根据十位进行排序,最后将元素从十位桶里取出来放入百位桶里,这样得到的元素就是排好序的序列。
void radix_Sort(SqList *L)
{/*基数排序,次数优先,如果是十进制数,那么就建立10个桶*/
typedef struct//定义桶
{
ElementType data[BudgetSize];
int NumElement;//桶中元素个数
}Budget;
typedef struct
{
Budget Budg[10];
} Pass;
Pass pass_single;//个位,十个桶每个桶里存放个位数为i的数据
Pass pass_decade;//十位
Pass pass_hundred;//百位
Budget pass[10];
for (int i = 0; i < BudgetSize; i++)//初始化
{
pass_single.Budg[i].NumElement = 0;
pass_decade.Budg[i].NumElement = 0;
pass_hundred.Budg[i].NumElement = 0;
}
int m;
for (int i = 0; i < L->Length; i++) //按个位入桶
{
int single = L->data[i] % 10;//求出各个位数
m = pass_single.Budg[single].NumElement;//当前桶内元素个数
pass_single.Budg[single].data[m] = L->data[i];
pass_single.Budg[single].NumElement++;
}
int n;
for (int i = 0; i < 10; i++)//把数从个位pass的各桶中取出来,放入pass_decade的桶
{
for (int j = 0; j < pass_single.Budg[i].NumElement; j++)
{
int decade = (pass_single.Budg[i].data[j]/10 )%10;//求十位数
n=pass_decade.Budg[decade].NumElement;
pass_decade.Budg[decade].data[n] = pass_single.Budg[i].data[j];
pass_decade.Budg[decade].NumElement++;
}
}
int k1;
for (int i = 0; i < 10; i++)//把数从十位pass的各桶中取出来,放入百位桶
{
for (int j = 0; j < pass_decade.Budg[i].NumElement; j++)
{
int hundred = pass_decade.Budg[i].data[j] / 100;//求百位数
k1 = pass_hundred.Budg[hundred].NumElement;
pass_hundred.Budg[hundred].data[k1] = pass_decade.Budg[i].data[j];
pass_hundred.Budg[hundred].NumElement++;
}
}
int k2 =0;
for (int t = 0; t < 10; t++)//把数从百位pass的各桶中取出来,放入顺序表List
{
for (int j = 0; j < pass_hundred.Budg[t].NumElement; j++)
{
L->data[k2++] = pass_hundred.Budg[t].data[j];
}
}
}
性能分析:这种方法是对简单的桶排序的一种改进。有效解决了M>>N的效率低问题。其算法复杂度为O(p(N+B)),P为pass的次数,N为待排序元素个数,B为桶的数目。额外的空间复杂度:O(N+B),B个桶加上把元素存在桶内
完整的测试代码
#include "pch.h"
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#define MaxSize 100
#define BudgetSize 10
typedef int ElementType;
typedef struct
{
ElementType data[MaxSize];
int Length;
}SqList;
void Swap(ElementType *a, ElementType *b)
{
ElementType tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
void DisplaySqList(SqList L)
/*打印序列*/
{
int i;
for (i = 0; i < L.Length; i++)
{
printf("%-4d", L.data[i]);
}
printf("\n");
}
ElementType ScanForMax(SqList L)
{
int pos = 0;
ElementType Temp;
for (int i = 0; i < L.Length; i++)
{
Temp = L.data[pos];
if (Temp < L.data[i])
pos = i;
}
return L.data[pos];
}
void Budget_Sort(SqList *L)
{/*桶排序,每个关键字对应一个桶*/
int NumBudget;
typedef struct Node
{/*定义一个链表*/
ElementType data;
struct Node *next;
}List;
typedef struct
{/*桶*/
List *Li;//存桶中元素链表
int NumElement;//桶中元素的个数
}Budget;
/*首先应该知道序列的最大值是多少,才能确定分配多少个桶,每个数据放入一个桶,每个桶可能放多个相同的数据*/
NumBudget = ScanForMax(*L);
Budget *Budg;
Budg = (Budget*)malloc(sizeof(Budget)*(NumBudget+1));
if (!Budg)//分配失败
exit(-1);
for (int i = 0; i <= NumBudget; i++)
{/*初始化每个桶*/
Budg[i].NumElement = 0;
Budg[i].Li = NULL;
}
int i = 0;
while (i<L->Length)
{/*依次读入每个元素,插入到相应的桶的链表第一个位置*/
List *p;
p= (List*)malloc(sizeof(List));//为链表申请一个节点空间
if (!p)
exit(-1);
p->data = L->data[i];
p->next = Budg[L->data[i]].Li;
Budg[L->data[i]].Li = p;//桶的链表头变成p
Budg[L->data[i]].NumElement++;//桶中的元素数量加1
i++;
}
int j=0;
List *p;
for (int i = 0; i <=NumBudget; i++)
{/*将桶中的数据取出来按顺序放入原顺序表*/
p = Budg[i].Li;//链表头指针
while (p)
{
L->data[j++] = p->data;
p = p->next;
}
}
}
void radix_Sort(SqList *L)
{/*基数排序,次数优先,如果是十进制数,那么就建立10个桶*/
typedef struct//定义桶
{
ElementType data[BudgetSize];
int NumElement;//桶中元素个数
}Budget;
typedef struct
{
Budget Budg[10];
} Pass;
Pass pass_single;//个位,十个桶每个桶里存放个位数为i的数据
Pass pass_decade;//十位
Pass pass_hundred;//百位
Budget pass[10];
for (int i = 0; i < BudgetSize; i++)//初始化
{
pass_single.Budg[i].NumElement = 0;
pass_decade.Budg[i].NumElement = 0;
pass_hundred.Budg[i].NumElement = 0;
}
int m;
for (int i = 0; i < L->Length; i++) //按个位入桶
{
int single = L->data[i] % 10;//求出各个位数
m = pass_single.Budg[single].NumElement;//当前桶内元素个数
pass_single.Budg[single].data[m] = L->data[i];
pass_single.Budg[single].NumElement++;
}
int n;
for (int i = 0; i < 10; i++)//把数从个位pass的各桶中取出来,放入pass_decade的桶
{
for (int j = 0; j < pass_single.Budg[i].NumElement; j++)
{
int decade = (pass_single.Budg[i].data[j]/10 )%10;//求十位数
n=pass_decade.Budg[decade].NumElement;
pass_decade.Budg[decade].data[n] = pass_single.Budg[i].data[j];
pass_decade.Budg[decade].NumElement++;
}
}
int k1;
for (int i = 0; i < 10; i++)//把数从十位pass的各桶中取出来,放入百位桶
{
for (int j = 0; j < pass_decade.Budg[i].NumElement; j++)
{
int hundred = pass_decade.Budg[i].data[j] / 100;//求百位数
k1 = pass_hundred.Budg[hundred].NumElement;
pass_hundred.Budg[hundred].data[k1] = pass_decade.Budg[i].data[j];
pass_hundred.Budg[hundred].NumElement++;
}
}
int k2 =0;
for (int t = 0; t < 10; t++)//把数从百位pass的各桶中取出来,放入顺序表List
{
for (int j = 0; j < pass_hundred.Budg[t].NumElement; j++)
{
L->data[k2++] = pass_hundred.Budg[t].data[j];
}
}
}
int main()
{
int A[] = { 200,51,123,3,5,234,126,233,45,78,1,0 };
int i, len;
len = sizeof(A) / sizeof(A[0]);
SqList L, L1, L2;
for (size_t i = 0; i < len; i++)
{
L.data[i] = A[i];
}
L.Length = len;
L1 = L2 = L;
printf("排序前:\n");
DisplaySqList(L);
Budget_Sort(&L);
printf("桶排序后:\n");
DisplaySqList(L);
printf("基数排序后:\n");
radix_Sort(&L1);
DisplaySqList(L1);
}
运行结果: