转载

计数排序、桶排序和基数排序

最近面试了一些人,发现大家都忽略了排序算法中的计数排序、桶排序和基数排序,segmentfault中都没有它们的标签就是一明证,呵呵!

计数排序

当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量内存。计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序中的算法来排序数据范围很大的数组。

算法的步骤如下:

1. 找出待排序的数组中最大和最小的元素

2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项

3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)

4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

#define NUM_RANGE (100)    //预定义数据范围上限,即K的值 void counting_sort(int *ini_arr, int *sorted_arr, int n)  //所需空间为 2*n+k {    int *count_arr = (int *)malloc(sizeof(int) * NUM_RANGE);    int i, j, k;    //初始化统计数组元素为值为零   for(k=0; k<NUM_RANGE; k++){      count_arr[k] = 0;    }    //统计数组中,每个元素出现的次数      for(i=0; i<n; i++){      count_arr[ini_arr[i]]++;    }    //统计数组计数,每项存前N项和,这实质为排序过程  for(k=1; k<NUM_RANGE; k++){      count_arr[k] += count_arr[k-1];    }    //将计数排序结果转化为数组元素的真实排序结果  for(j=n-1 ; j>=0; j--){        int elem = ini_arr[j];    //取待排序元素      int index = count_arr[elem]-1;  //待排序元素在有序数组中的序号      sorted_arr[index] = elem; //将待排序元素存入结果数组中      count_arr[elem]--;  //修正排序结果,其实是针对算得元素的修正  }    free(count_arr);   }    

桶排序

我的理解:桶排序是计数排序的变种,把计数排序中相邻的m个"小桶"放到一个"大桶"中,在分完桶后,对每个桶进行排序(一般用快排),然后合并成最后的结果。

基本思想:桶排序假设序列由一个随机过程产生,该过程将元素均匀而独立地分布在区间[0,1)上。我们把区间[0,1)划分成n个相同大小的子区间,称为桶。将n个记录分布到各个桶中去。如果有多于一个记录分到同一个桶中,需要进行桶内排序。最后依次把各个桶中的记录列出来记得到有序序列。

效率分析:桶排序的平均时间复杂度为线性的O(N+C),其中C为桶内快排的时间复杂度。如果相对于同样的N,桶数量M越大,其效率越高,最好的时间复杂度达到O(N)。 当然桶排序的空间复杂度 为O(N+M),如果输入数据非常庞大,而桶的数量也非常多,则空间代价无疑是昂贵的。此外,桶排序是稳定的。

基数排序

基本思想:将待排数据中的每组关键字依次进行桶分配。

具体示例:

278、109、063、930、589、184、505、269、008、083

我们将每个数值的个位,十位,百位分成三个关键字: 278 -> k1(个位)=8 ,k2(十位)=7 ,k3=(百位)=2。

然后从最低位个位开始(从最次关键字开始),对所有数据的k1关键字进行桶分配(因为,每个数字都是 0-9的,因此桶大小为10),再依次输出桶中的数据得到下面的序列。

930、063、083、184、505、278、008、109、589、269

再对上面的序列接着进行针对k2的桶分配,输出序列为:

505、008、109、930、063、269、278、083、184、589

最后针对k3的桶分配,输出序列为:

008、063、083、109、184、269、278、505、589、930

效率分析:

基数排序的性能比桶排序要略差。每一次关键字的桶分配都需要O(N)的时间复杂度,而且分配之后得到新的关键字序列又需要O(N)的时间复杂度。假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是O(d*2N) ,当然d要远远小于N,因此基本上还是线性级别的。基数排序的空间复杂度为O(N+M),其中M为桶的数量。一般来说N>>M,因此额外空间需要大概N个左右。

但是,对比桶排序,基数排序每次需要的桶的数量并不多。而且基数排序几乎不需要任何“比较”操作,而桶排序在桶相对较少的情况下,桶内多个数据必须进行基于比较操作的排序。因此,在实际应用中,基数排序的应用范围更加广泛。

正文到此结束
Loading...