分配排序 总结
前言
分配排序的基本思想:排序过程无须比较关键字,而是通过“分配”和“收集”过程来实现排序。
分配排序常见的算法有桶排序和基数排序。
一、桶排序
1、桶排序的基本思想
桶排序(Bucket Sort)也称箱排序(Bin Sort),其基本思想是:设置若干个桶,依次扫描待排序的记录R[0],R[1],…R[n-1],把关键字等于k的记录全都装入到第k个桶里(分配),然后按序号依次将各非空的桶首尾连接起来(收集)。
桶排序的思想是把[0,1)划分为n个大小相同的子区间,每一子区间是一个桶。然后将n个记录分配到各个桶中。因为关键字序列是均匀分布在[0,1)上的,所以一般不会有很多个记录落入同一个桶中。由于同一桶中的记录其关键字不尽相同,所以必须采用关键字比较的排序方法(通常用插入排序)对各个桶进行排序,然后依次将各非空桶中的记录连接(收集)起来即可。
2、桶排序算法的核心
1)分配过程
2)排序过程
3)收集过程
3、桶排序算法的示例
伪算法:
void BucketSort(int a[], int n){// 对R[0..n-1]做桶排序,其中0<=R[i]<=1 (0<=i<n)
for (int i=0; i<n; i++) // 分配过程{将R[i]插入到桶B[R[i]]中;}for (i=0; i<n; i++) // 排序过程{当B[i]非空时,用插入排序将B[i]中的记录排序;}for (i=0; i<n; i++) // 收集过程{若B[i]非空,则将B[i]中的记录一次输出到a中;}}
下图表示出了桶排序作用于有10个数的输入数组上的操作过程。
4、桶排序算法的实现
#include <time.h>#include <iostream>#include <iomanip>using namespace std;
/*initial arr*/
void InitialArr(double *arr,int n){srand((unsigned)time(NULL));
for (int i = 0; i<n;i++){arr[i] = rand()/double(RAND_MAX+1); //(0.1)}}
输出数组:
/* print arr*/
void PrintArr(double *arr,int n){for (int i = 0;i < n; i++){cout<< arr[i] << " ";
if ((i+1)%5 == 0 || i == n-1)
{cout<<endl;}}}
桶排序:
void BucketSort(double arr[], int n){int i;
double **bucket = new double *[10];int count[10] = {0};
for (i=0; i<10; i++)
{bucket[i] = new double[n];}/* 对R[0..n-1]做桶排序,其中0<=R[i]<=1 (0<=i<n)*/
// 分配过程
for (i=0; i<n; i++)
{// 将R[i]插入到桶B[R[i]]中;
double temp = arr[i];
int flag = (int)(arr[i]*10); // flag标识小树的第一位bucket[flag][count[flag]] = temp; // 用二维数组的每个向量来存放小树第一位相同的数据
int j = count[flag]++;
/* 利用插入排序对每一行进行排序 */
// 排序过程
for(;j > 0 && temp < bucket[flag][j - 1]; --j)
{bucket[flag][j] = bucket[flag][j-1];}bucket[flag][j] =temp;}/* 所有数据重新链接 */
// 收集过程
int k=0;
for (i = 0 ; i < 10 ; i++)
{for (int j = 0 ; j< count[i];j++){arr[k] = bucket[i][j];k++;}}// 释放内存资源
for (i = 0 ; i<10 ;i++)
{delete bucket[i];
bucket[i] =NULL;}delete []bucket;
bucket = NULL;}
主函数调用:
int main(void){double *arr=new double[10];InitialArr(arr, 10);BucketSort(arr, 10);PrintArr(arr,10);delete [] arr;
return 0;
}
5、桶排序算法的性能分析
桶排序是稳定的。
桶排序的平均时间复杂度为线性O(N+C),其中C=N*(logN-logM)。如果相对于同样的N,桶数量M越大,其效率越高,最好的时间复杂度达到O(N)。当然桶排序的空间复杂度为O(N+M),如果输入数据非常庞大,而桶的数量也非常多,则空间代价无疑是昂贵的。
参考:http://blog.csdn.net/heaven13483/article/details/7716829
二、基数排序
1、基数排序的基本思想
基数排序的基本思想是:从低位到高位依次对(j=d-1,d-2,..,0)进行箱排序。在第d趟箱排序汇总,所需的箱子数就是基数rd,这就是“基数排序”名称的由来。
2、基数排序的示例
基数排序的方法是将数据按位分开,并从数据的最低有效位到最高的有效位进行比较依次排序,从而得到有序的集合。我们来看一个简单的例子:{15,12,49,16,36,40};
在对个位进行排序之后,其结果是{40,12,15,16,36,49};
在对十位进行排序后,其结果是{12,15,16,36,40,49};
有一点很重要就是必须保证在对每个位进行排序时,其排序过程必须是稳定的。也就是说,数据在进行较低位排序之后,此数据的位置不应该改变,除非进行较高位比较时需要对它进行调整。也就是例子中,个为排序后是 12 15 16 在进行十位排序时,十位都是1,这时,不应该对其进行调整。
3、基数排序算法的实现
基数排序是通过对各个元素每个位上的数来进行排序从而实现排序的。
首先必须统一所有元素的数位长度,数位较短的在前面补0.
然后从最低位开始,依次对每个位进行排序,当从最低位到最高位都排序完以后,便可得到一个有序序列了。
至于这个方法正确性的证明可以看下算法导论。
对每个位的排序可以采用计数排序实现,这时K直接取9即可。
/**
显示数据*/void display (int a[], int n){for (int i = 0; i < n; i++)cout << a[i] << " ";
cout << endl;}/**
求出这段序列中最大的数*/int Mymax (int a[], int n){int m = a[0];
for (int i = 1; i < n; i++){if (m < a[i])
m = a[i];}return m;
}/**
求出某个数的最大位数*/int max_bit (int num){int bits = 1;
while (num / 10)
{bits++;num /= 10;}return bits;
}
/**
基数排序*/void radix_sort (int a[], int n){int* b;
int c[10];
int d;
int t;
int radix = 1;
b = new int[n]d = max_bit (Mymax (a, n)); // 得到这个序列最大的位数
for (int i=0; i<d; i++,radix *= 10) // 按照每一位进行排序{/* use counting_sort to sort the array by specific digit */
int j;
for (j=0; j<10; j++) // 将某一位的容量置0,比如个位可能出现0,1...9这些数c[j] = 0;for (j=0; j<n; j++) // 在某一位上对所有数进行统计,并分配到每个桶里{t = a[j] / radix % 10;c[t]++;}for (j=1; j<10; j++) // 累计c[j] += c[j - 1];for (j=n-1; j>=0; j--) // 在某位上按照桶的序号进行排序{t = a[j] / radix % 10;b[c[t] - 1] = a[j];c[t]--;}/* copy the sorted array from b to a */
for (j=0; j<n; j++)
a[j] = b[j];}delete [] b;
}
4、基数排序算法的性能分析
基数排序的平均时间复杂度和最坏时间复杂度都为,当然基数排序的空间复杂度为O(N),
不过基数排序是稳定的。