1.关于单趟桶式排序的介绍以及问题所在
介绍一个简单的情况,假如我们有若干个整数(各不相同),这些整数的范围在0-99内,如何排好这若干个整数呢?
按照桶式排序的方法,我们可以设置一个数组int barrel[100],每个元素代表一个桶,每个桶的下标代表这个桶里装的数字,比如数字18,我们就把它装进barrel[18]里面去。所以,不难得到,如果待排序的数字序列有40个数字,那么我们只需要花费40次把数字装进桶里的时间就可以了。
问题在于,如果数字序列中只有2个数字,可由于不知道到底是0-99中的哪两个,所以仍然需要开辟100个元素的数组空间用来当桶,这就造成了空间的浪费。所以我们考虑桶越少越好。
另外,我们上面考虑的是数字序列中的数字各不相同的情况,如果考虑重复的话,我们可能需要数组来表示桶,才能装得下重复的数字。就拿上面的例子举例,假设待排序列中只有2个数(范围在0-99间),由于每个桶都有可能装这两个数,所以每个桶都应该是大小为2的数组,又因为要设置100个桶,所以空间花费就为2×100个元素类型的空间。当待排序列中有n个数时,空间花费将变成n×100。而且要知道真正装有数字的空间只有n个,所以有大量的空间因为没有装数字所以导致了巨大的浪费。于是我们考虑每个桶用链表来表示,有就分配,没有就不分配,从而避免不必要的空间浪费。
2.解释多趟桶式排序
多趟桶式排序的每一趟就是对某一个位进行桶式排序,每一趟把某个数按照某一位上的数字放入桶中。
究竟是从低位到高位,还是从高位到低位呢?
首先我们要知道每趟把数字放入桶里后要做什么。当我们将数字依次放入桶里后,我们要将桶里的数字“倒出来”,就是把它按顺序覆盖原来的数字序列,作为下一趟的待处理数字序列。“倒出来”的过程中,数字不同的桶好处理,只要按照桶的编号从小到大倒就可以了,但是一个桶里的不同数字如何处理呢?
不难得出,此时我们应该把同一个桶里的数字按照次位的数字大小排好序,小的放在这个桶的前面,大的放在这个桶的后面,然后只需要在倒桶的时候按照从前到后的顺序倒出去就ok了。而由于同一个桶中某两个数字的先后顺序是由处理这个两个数字的先后顺序决定的(就是先处理的先放,后处理的后放),而处理的顺序就是这个两个数字在本趟的待处理序列中的先后顺序。也就意味着,本趟的待处理序列必须已经按照次位的顺序排好了。于是就要求我们的多趟桶式排序应该是从低位到高位一趟一趟来。
下面举个例子来展示上面的解释,便于理解:
3.桶式排序的代码实现
#include "List.h"
void Initial(List barral[])
{
int i = 0;
for (i = 0; i < 10; i++)
barral[i] = MakeList();
}
void PrintNum(List L)
{
Position islast=Last(L);
Position P = L->next;
while (P != islast)
{
printf("%d ", P->number);
P = P->next;
}
}
int main()
{
int addone;
int i, j;
Position P;
/*
设置木桶,每个桶是一个链表,下面是对木桶含义的解释:
确定一位(个位、十位或者百位),10个元素分别代表10个木桶,每一个木桶用来装在这位上数字相同的三位数.
如:个位设置10个木桶,假设有116和226这两个数字,则我们把它们装在这10个桶中的6号桶(barrel[6])里
*/
List barral[10] = {
0 };
Initial(barral);
//创建一个链表用来装待排序的数字
List number=MakeList();
printf("请输入多个三位数以内的数字,我们将为你排序:>\n");
//ctrl+z结束输入
while (scanf("%d", &addone) != EOF)
{
//将待排序的数字插入number这个链表中
Insert(number, Last(number), addone);
}
//j表示当前处理的位,从个位先开始(j=1:个位、j=2:十位、j=3:百位)将每个待排序的数字按照当前位上的数字,装进这个位中的10个桶中的一个
for (j = 1; j <= 3;j++)
{
//遍历处理每一个number中的待排序数字
for (P = number->next; P != NULL; P = P->next)
{
//m表示当前处理位的权重(如:j=1表示个位,那么个位权重就是1,即m=1)
int m=(int)pow(10,j-1);
//i表示这个待处理的三位数在当前位上的数字
int i = ((P->number) / m)%10;
//将其放到当前位对应的桶里面去(放到对应桶的末尾)
Insert(barral[i], Last(barral[i]), P->number);
}
/*
至此所有数字都按照当前位的数字放入了当前位的10个桶中对应的某一个桶里去了,接下来更新装有待排序的三位数的number链表的顺序
*/
//首先将旧的number链表清空
MakeEmpty(number);
//然后把木桶里按照当前位的数字大小装好的三位数按从小到大依次放入number链表中,作为下一次循环的待排序数字序列
//这里的i指的是当前位数字为i的木桶,这里是遍历i,将每个i对应的木桶里的三位数放入number链表中
for (i = 0; i < 10; i++)
{
P = barral[i]->next;
while (P)
{
Insert(number, Last(number), P->number);
P = P->next;
}
/*
barral[i]这个木桶里的三位数都放到number里去后,就把它清空以供下一次循环使用(下一个循环中,作为装下一位数字为i的三位数的桶)
比如:barral[1]在当前这个外层循环中表示个位数字为1的桶,等到下一个外层循环后,barral[1]便作为十位数字为1的桶了
*/
MakeEmpty(barral[i]);
}
}
//等到个位十位百位依次排序后,最后number中的顺序就是从小到大的顺序了
PrintNum(number);
return 0;
}
List.h和List.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<math.h>
#ifndef LIST_H
#define LIST_H
struct Node;
typedef struct Node* ptrNode;
typedef ptrNode Position;
typedef ptrNode List;
List MakeList();
void MakeEmpty(List L);
int IsEmpty(List L);
int Find(List L, Position P);
Position FindPositon(List L, int x);
Position FindPrevious(List L, int check);
Position First(List L);
Position Last(List L);
void Insert(List L, Position P,int addone);
void Delete(List L, int popone);
struct Node{
int number;
Position next;
};
#endif
#include "List.h"
List MakeList()
{
List L = (List)malloc(sizeof(struct Node));
L->next = NULL;
return L;
}
void MakeEmpty(List L)
{
Position P=L->next;
Position tmp;
L->next = NULL;
while (P)
{
tmp = P;
P = P->next;
free(tmp);
}
}
int IsEmpty(List L)
{
return (!L->next)?1:0;
}
Position First(List L)
{
return L->next;
}
Position Last(List L)
{
Position P = L;
while (P->next!=NULL)
P = P->next;
return P;
}
int Find(List L, Position P)
{
Position tmp = L->next;
while (tmp != NULL&&tmp != P)
P = P->next;
if (tmp)
return 1;
else
return 0;
}
Position FindPosition(List L, int x)
{
int i=1;
Position P = L->next;
while (P && i != x)
{
P = P->next;
x++;
}
return P;
}
Position FindPrevious(List L, int check)
{
Position P = L;
while ( P->next && P->next->number != check)
P = P->next;
if (!P->next)
return NULL;
else
return P;
}
void Insert(List L, Position P, int addone)
{
Position tmp;
tmp = malloc(sizeof(struct Node));
if (tmp != NULL)
{
tmp->next = P->next;
tmp->number = addone;
P->next = tmp;
}
}
void Delete(List L, int popone)
{
Position P = FindPrevious(L, popone);
if (P)
{
Position tmp = P->next;
P->next = tmp->next;
free(tmp);
}
}