所谓桶排序:
是先创造一个长度为10的count数组,来存放数组元素。首先,计算出数组最大数的最高位的位数记作Digit。从个位也就是右数第一位开始,把数组从左往右依次遍历。根据右数第一位的数字把元素放在不同的桶里。
int[] a = new int[] {13,24, 16, 29, 16, 50, 20, 53, 32, 71};
上面这个数组在digit = 1的情况则是把元素依次放在3,4,6,9,6,0,0,3,2,1号桶中,并让那个桶的count[桶号]++,放入桶的操作实际上就是把元素按照个位大小进行了一次排序。
然后求count的左和,也就是把小的桶的计数加在大的桶上。这个例子中,0号桶0+2个,1号桶2+1个数,2号3+1个,3号4+2个,4号6+1个,5号7+0个,6号7+2个,7、8号9+0个,9号9+1个。
count[i]的左和实际上是表示该位小于等于数字 i 的有多少个。例:count[3]表示个位数小于等于3的数的个数
再根据a数组从右往左遍历,遍历到的数用一个帮助bucket[]数组来存储,存储在bucket数组里下标为count[ j ]-1 的位置并且让count[ j ]--,j为该数的第d位的值 这次遍历是个位数的值。
为什么要从右往左取出桶里的数呢? 这样可以保证在从桶中取出的时候不会乱序(先放在bucket下标大的,然后count--,下次放相同位的时候就是放在它之前)
辅助数组放满之后,把他复制给a数组。然后再次循环上述操作,直到把最高位的排序也执行完。
桶排序就结束了。
代码如下:
static void Main(string[] args)
{
int[] a = new int[] { 13,24, 16, 29, 16, 50, 20, 53, 32, 71 };
RadixSort(a, 0, a.Length-1, Maxbits(a));
foreach (int item in a)
{
Console.WriteLine(item);
}
}
//数组最大数的最高位数
static int Maxbits(int[] arr)
{
int max = int.MinValue;
for (int i = 0; i < arr.Length; i++)
{
max = Math.Max(max, arr[i]);//选出数组中的最大值
}
int res = 0;
while (max != 0)
{
res++;
max /= 10;
}
return res;
}
//一个数的第d位是多少
static int GetDigit(int x, int d)
{
return x / ((int)Math.Pow(10, d - 1)) % 10;
}
//分片排序、桶排序
public static void RadixSort(int[] arr, int l, int r, int digit)//digit表示最高位数
{
int radix = 10;//分10个桶
int i = 0, j = 0;
int[] bucket = new int[r - l + 1];
//最高位有几位就几次循环
for (int d = 1; d <= digit; d++)
{
//定义10个桶
int[] count = new int[radix];
//count[0] 当前位 d 是 0 的数字有多少个
//count[i] 当前位 d 是(0~i)的数字有多少个
//把数组某一位的数进行计数
for (i = l; i <= r; i++)
{
//j为当前数字的第 d 位的 数字
j = GetDigit(arr[i], d);
//这个数字的计数++
count[j]++;
}
//计算小和
for (i = 1; i < radix; i++)
{
count[i] = count[i - 1] + count[i];
}
for (i = r; i >= l; i--)
{
//j为从右往左遍历每个数组元素的第d位 的数字
j = GetDigit(arr[i], d);
//该数字下标的 count计数-- 并将该数字对应的 数组元素 装进辅助数组里
bucket[count[j] - 1] = arr[i];
count[j]--;
}
//再把辅助数组复制给原数组
for (i = l, j = 0; i <= r; i++, j++)
{
arr[i] = bucket[j];
}
}
}