最近面试了一些人,发现你们都忽略了排序算法中的计数排序、桶排序和基数排序,segmentfault中都没有它们的标签就是一明证,呵呵!面试
当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。算法
因为用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,须要大量内存。计数排序是用来排序0到100之间的数字的最好的算法,可是它不适合按字母顺序排序人名。可是,计数排序能够用在基数排序中的算法来排序数据范围很大的数组。segmentfault
算法的步骤以下:数组
#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个"小桶"放到一个"大桶"中,在分完桶后,对每一个桶进行排序(通常用快排),而后合并成最后的结果。code
基本思想:排序
桶排序假设序列由一个随机过程产生,该过程将元素均匀而独立地分布在区间[0,1)上。咱们把区间[0,1)划分红n个相同大小的子区间,称为桶。将n个记录分布到各个桶中去。若是有多于一个记录分到同一个桶中,须要进行桶内排序。最后依次把各个桶中的记录列出来记获得有序序列。内存
效率分析:效率
桶排序的平均时间复杂度为线性的O(N+C),其中C为桶内快排的时间复杂度。若是相对于一样的N,桶数量M越大,其效率越高,最好的时间复杂度达到O(N)。 固然桶排序的空间复杂度 为O(N+M),若是输入数据很是庞大,而桶的数量也很是多,则空间代价无疑是昂贵的。此外,桶排序是稳定的。统计
基本思想:
将待排数据中的每组关键字依次进行桶分配。
具体示例:
27八、10九、06三、930、58九、18四、50五、26九、00八、083
咱们将每一个数值的个位,十位,百位分红三个关键字: 278 -> k1(个位)=8,k2(十位)=7,k3=(百位)=2。
而后从最低位个位开始(从最次关键字开始),对全部数据的k1关键字进行桶分配(由于,每一个数字都是 0-9的,所以桶大小为10),再依次输出桶中的数据获得下面的序列。
930、06三、08三、18四、50五、27八、00八、10九、58九、269
再对上面的序列接着进行针对k2的桶分配,输出序列为:
50五、00八、10九、930、06三、26九、27八、08三、18四、589
最后针对k3的桶分配,输出序列为:
00八、06三、08三、10九、18四、26九、27八、50五、58九、930
效率分析:
基数排序的性能比桶排序要略差。每一次关键字的桶分配都须要O(N)的时间复杂度,并且分配以后获得新的关键字序列又须要O(N)的时间复杂度。假如待排数据能够分为d个关键字,则基数排序的时间复杂度将是O(d*2N) ,固然d要远远小于N,所以基本上仍是线性级别的。基数排序的空间复杂度为O(N+M),其中M为桶的数量。通常来讲N>>M,所以额外空间须要大概N个左右。
可是,对比桶排序,基数排序每次须要的桶的数量并很少。并且基数排序几乎不须要任何“比较”操做,而桶排序在桶相对较少的状况下,桶内多个数据必须进行基于比较操做的排序。所以,在实际应用中,基数排序的应用范围更加普遍。