桶排序是一种神奇的算法。
问题:
假设有数组:arr[] = { 32, 33, 23, 42, 12, 54, 53, 52, 100 };
该如何用桶排序?
第一轮:
- 准备十个桶子
- 从前向后遍历,将个位为x的数字依次放入第x个桶子中,即:
桶0:100
桶1:
桶2:32, 42, 12, 52
桶3:33, 23, 53
桶4:54
桶5:
……
- 将所有桶中元素依次倒出至原数组:arr[] = { 100, 32, 42, 12, 52, 33, 23, 53, 54 };
第二轮:
- 从前向后遍历,将十位为x的数字依次放入第x个桶子中,即:
桶0:100
桶1:12
桶2:23
桶3:32, 33
桶4:42
桶5:52, 53, 54
……
- 将所有桶中元素依次倒出至原数组:arr[] = { 100, 12, 23, 32, 33, 42, 52, 53, 54 }
第三轮:
- 从前向后遍历,将百位为x的数字依次放入第x个桶子中,即:
桶0:12, 23, 32, 33, 42, 52, 53, 54
桶1:100
桶2:
……
- 将所有桶中元素依次倒出至原数组:arr[] = { 12, 23, 32, 33, 42, 52, 53, 54, 100 }
实现:
具体实现稍有区别
- 如果对于给出的数组arr而言,为了使代码简洁易于管理,不应该分别创建十个桶,分别装每一个数字,那样会使代码冗余且难以管理,空间复杂度上升,所以我们采用遍历数组记录数组中数字的 个位/十位/百位 的个数,再并从后向前遍历,将对应的元素放入本次桶装后倒出后元素该存在的位置。
- 动态分配一个pa空间,其大小为传入数组的大小,专门用来存放每次排序后的数组。
int *pa = (int*)malloc(size * sizeof(int));
- 创建一个count[10]数组,下标的位置专门用来存放该桶应该放入的数字个数,比如:
上面的例子中:arr[] = { 32, 33, 23, 42, 12, 54, 53, 52, 100 }; (size == 9)
则个位数入桶时:count = { 1, 0, 4, 3, 1, 0, 0, 0, 0, 0 };
对这个数组稍加变动,变为每次count中某一位 ++ 时,后面的每一位都++,则:
- 表示:每次下标位变动时第[n]位的值,即第n桶内最后一个元素在数组中所排的位置。
个位为例:
桶0:100 //存一个数字
桶1:
桶2:32, 42, 12, 52 //存四个数字
桶3:33, 23, 53 //存三个数字
桶4:54 //存一个数字
桶5:
……
从后向前遍历时,检查每个个位的数字:
- 遇到100,将它放在数组中第 count[0] - 1 == 0位,且count[0]-- == 0
此时pa == {100, 0, 0, 0, 0, 0, 0, 0, 0 }; count[] == { 1, 1, 5, 8, 9, 9, 9, 9, 9, 9 };
- 遇到52,将它放在数组中第 count[2] - 1 == 4位,且count[2]-- == 4
此时pa == {100, 0, 0, 0, 52, 0, 0, 0, 0 }; count[] == { 1, 1, 4, 8, 9, 9, 9, 9, 9, 9 };
- 遇到53,将它放在数组中第 count[3] - 1 == 7位,且count[3]-- == 7
此时pa == {100, 0, 0, 0, 52, 0, 0, 53, 0 }; count[] == { 1, 1, 4, 7, 9, 9, 9, 9, 9, 9 };
- 遇到54,将它放在数组中第 count[4] - 1 == 8位,且count[4]-- == 8
此时pa == {100, 0, 0, 0, 52, 0, 0, 53, 54 }; count[] == { 1, 1, 4, 7, 8, 9, 9, 9, 9, 9 };
- 遇到12,将它放在数组中第 count[2] - 1 == 3位,且count[2]-- == 3
此时pa == {100, 0, 0, 12, 52, 0, 0, 53, 54 }; count[] == { 1, 1, 3, 7, 8, 9, 9, 9, 9, 9 };
- 遇到42,将它放在数组中第 count[2] - 1 == 2位,且count[2]-- == 2
此时pa == {100, 0, 42, 12, 52, 0, 0, 53, 54 }; count[] == { 1, 1, 2, 7, 8, 9, 9, 9, 9, 9 };
- 遇到23,将它放在数组中第 count[3] - 1 == 6位,且count[3]-- == 6
此时pa == {100, 0, 42, 12, 52, 0, 23, 53, 54 }; count[] == { 1, 1, 2, 6, 8, 9, 9, 9, 9, 9 };
- 遇到33,将它放在数组中第 count[3] - 1 == 5位,且count[3]-- == 5
此时pa == {100, 0, 42, 12, 52, 33, 23, 53, 54 }; count[] == { 1, 1, 2, 5, 8, 9, 9, 9, 9, 9 };
- 遇到32,将它放在数组中第 count[2] - 1 == 1位,且count[2]-- == 1
此时pa == {100, 32, 42, 12, 52, 33, 23, 53, 54 }; count[] == { 1, 1, 1, 5, 8, 9, 9, 9, 9, 9 };
这样我们就可以通过从后向前遍历的方式来判断每个数字入桶时的位置。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include <stdlib.h>
#include <malloc.h>
//桶排序
void RadixSort(int *arr, int size)
{
int *pa = (int*)malloc(size * sizeof(int));
memset(pa, 0, size * sizeof(int));
int count[10] = { 0 };
int boundary = 0;
int temp = 0;
int i = 0;
//最大值
for (i = 0; i < size; i++)
{
boundary = abs(arr[i]) > abs(boundary) ? arr[i] : boundary;
}
//求最大数字的十进制位数
i = 0;
while (boundary)
{
i += boundary? 1 : 0;
boundary /= 10;
}
boundary = i;
for (i = 0; i < boundary; i++)
{
int j = 0;
//标号统计
for (j = 0; j < size; j++)
{
temp = arr[j] / (int)pow(10, i);
temp = temp % 10;
while (temp < 10)
{
count[temp++]++;
}
}
//入桶:
for (j = size - 1; j >= 0; j--)
{
temp = arr[j] / (int)pow(10, i);
temp = temp % 10;
pa[--count[temp]] = arr[j];
}
//出桶:
memcpy(arr, pa, size * sizeof(int));
//清空:
memset(pa, 0, size * sizeof(int));
for (j = 0; j < 10; j++)
{
count[j] = 0;
}
}
free(pa);
pa = NULL;
}
int main()
{
int arr[] = { 32, 33, 23, 42, 12, 54, 53, 52, 100};
int size = sizeof(arr) / sizeof(arr[0]);
RadixSort(arr, size);
for (int i = 0; i < size; i++)
{
printf("%d ", arr[i]);
}
system("pause");
return 0;
}