线性排序之基数排序,桶排序,计数排序

    • 基数排序
    • 计数排序
    • 桶排序

基数排序,桶排序,计数排序是三种线性排序方法,突破了比较排序的O(nlogn)的限制。但是只适用于特定的情况。

基数排序

以下为维基百科的描述:

基数排序 :

将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant

digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

基数排序的效率:

基数排序的时间复杂度是O(k·n),其中n是排序元素个数,k是数字位数。注意这不是说这个时间复杂度一定优于O(n·log(n)),k的大小取决于数字位的选择(比如比特位数),和待排序数据所属数据类型的全集的大小;k决定了进行多少轮处理,而n是每轮处理的操作数目。

以排序n个不同整数来举例,假定这些整数以B为底,这样每位数都有B个不同的数字,k =

logB(N),N是待排序数据类型全集的势。虽然有B个不同的数字,需要B个不同的桶,但在每一轮处理中,判断每个待排序数据项只需要一次计算确定对应数位的值,因此在每一轮处理的时候都需要平均n次操作来把整数放到合适的桶中去,所以就有:

k约等于logB(N) 所以,基数排序的平均时间T就是:

T~= logB(N)·n 其中前一项是一个与输入数据无关的常数,当然该项不一定小于logn

如果考虑和比较排序进行对照,基数排序的形式复杂度虽然不一定更小,但由于不进行比较,因此其基本操作的代价较小,而且在适当选择的B之下,k一般不大于logn,所以基数排序一般要快过基于比较的排序,比如快速排序。

简单来说,基数排序就是从最低位排序一直到最高位排序完成。时间复杂度O(kn) 空间复杂度O(n)

#include <iostream>
using namespace std;
int maxbit(int data[], int n)
{
    int d = 1;
    int max = data[0];
    for (int i = 1; i < n; i++) {  // 找出最大值
        if (data[i] > max)
            max = data[i];
    }
    int radix = 10;
    while (max >= radix) {
        d++;
        radix *= 10;
    }
    return d;
}
void radixsort(int data[], int n) {
    int d = maxbit(data, n); // 求data中最大数具有的位数,决定循环几次
    int *count = new int[10]; // 10 个桶
    int *tmp = new int[n];
    int radix = 1;
    for (int i = 0; i < d; i++) {
        // 每次清零桶计数器
        for (int j = 0; j < 10; j++)
            count[j] = 0;
        // 统计每个桶中的记录数
        for (int j = 0; j < n; j++) {
            int k = (data[j] / radix) % 10;
            count[k]++;
        }
        // 分配tmp的位置
        for (int j = 1; j < 10; j++) {
            count[j] = count[j - 1] + count[j]; // count[j] 中 是 j 桶 和比 j 小的桶中的记录数的和
        }
        // 倒着来,将所有桶中记录依次收集到tmp中
        for (int j = n - 1; j >= 0; j--) {
            int k = (data[j] / radix) % 10;
            tmp[count[k] - 1] = data[j];
            count[k]--;
        }
        // 将临时数组复制回data
        for (int j = 0; j < n; j++)
            data[j] = tmp[j];
        radix *= 10;
    }
    delete count;
    delete tmp;
    count = NULL;
    tmp = NULL;
}
int main()
{
    int data[] = { 52,353,458,279,129,152 };
    radixsort(data, 6);
    for (int i = 0; i < 6; i++)
        cout << data[i] << " ";
}

计数排序

维基百科-计数排序

计数排序对于数据范围很大的数组,需要大量时间和内存。适合排序小范围内的整数。

算法的步骤如下:

  • 找出待排序的数组中最大和最小的元素
  • 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
  • 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  • 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
void count_sort(int data[], int n) {
    int *tmp = new int[n];
    int max = data[0]; // max为data中的最大数
    int min = data[0];
    for (int i = 0; i < n; i++) {
        if (data[i] > max)
            max = data[i];
        if (data[i] < min)
            min = data[i];
    }
    int len = max - min + 1;
    int *count = new int[len];
    for (int i = 0; i < len; i++)
        count[i] = 0;
    for (int i = 0; i < n; i++)
        count[data[i]-min]++;
    for (int j = 1; j < len; j++)
        count[j] += count[j - 1];
    for (int i = n - 1; i >= 0; i--) {
        tmp[count[data[i]-min] - 1] = data[i];
        count[data[i]-min]--;
    }
    for (int i = 0; i < n; i++)
        data[i] = tmp[i];
    delete count;
    delete tmp;
}

桶排序

假设排序数的范围是[0,1],在[0,1]内平均划分为k个桶,将待排序数放在这些桶里。对于桶内元素个数大于 1 的进行桶内排序(常用插入排序)。因为平均划分,所以一个桶内的元素不会太多,时间复杂度O(n),点击打开维基百科-桶排序

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-06 00:53:27

线性排序之基数排序,桶排序,计数排序的相关文章

排序算法的c++实现——计数排序

任何比较排序算法的时间复杂度的上限为O(NlogN), 不存在比o(nlgN)更少的比较排序算法.如果想要在时间复杂度上超过O(NlogN)的时间复杂度,肯定需要加入其它条件.计数排序就加入了限制条件,从而使时间复杂度为O(N). 计数排序的核心思想(来自算法导论):计数排序要求待排序的n个元素的大小在[0, k]之间,并且k与n在一个数量级上,即k=O(n).对于每一个输入元素x, 确定小于等于x的个数为i.利用这一信息,就可以把元素x放到输出数组的正确位置,即把元素x放到输出数组下标为i-1

算法总结——三大排序(快排,计数排序,归并)

快排: 适用条件:方便...只要数字不是很多 复杂度:O(nlogn)  每一层n复杂度,共logn层 原理:利用一个随机数与最后面一个数交换,那么这个随机数就到了最后一位,然后循环,如果前面的数大于最后一个数,那么把这个数放到前面去,经过一次排序之后,前面的数都是大于最后一个的,然后对1到k和k+1到n进行排序,一层一层地下去 模板: #include<cstdio> #include<algorithm> #include<time.h> using namespa

计数排序+基数排序

这几天一直在写排序算法,从插入,冒泡,选择到归并和快速再到计数和基数排序.今天来写下计数排序和基数排序吧. 计数排序:对于一组小于k的数组,进行排序.这里要保证输入的关键值在[0..k]之间.貌似很简单,我们先不管什么是计数排序CountSort(A,1,n),先来看一下一段代码. CountPrint(int *A,int n,int k)代码: void CountPrint(int *A,int n,int k){//输入数组A,每个元素都小于k //C[i]用来计算i出现的次数,起初C[

线性排序算法---- 计数排序, 基数排序, 桶排序

排序算法里,除了比较排序算法(堆排序,归并排序,快速排序),还有一类经典的排序算法-------线性时间排序算法.听名字就让人兴奋! 线性时间排序,顾名思义,算法复杂度为线性时间O(n) , 非常快,比快速排序还要快的存在,简直逆天.下面我们来仔细看看三种逆天的线性排序算法, 计数排序,基数排序和桶排序. 1计数排序  counting Sort 计数排序 假设 n个 输入元素中的每一个都是在 0 到 k 区间内的一个整数,当 k= O(N) 时,排序运行的时间为 O(N). 计数排序的基本思想

大话桶排序 基数排序和计数排序

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

计数排序和基数排序的实现

计数排序 计数排序的原理 设被排序的数组为A,排序后存储到B,C为临时数组.所谓计数,首先是通过一个数组C[i]计算大小等于i的元素个数,此过程只需要一次循环遍历就可以:在此基础上,计算小于或者等于i的元素个数,也是一重循环就完成.下一步是关键:逆序循环,从length[A]到1,将A[i]放到B中第C[A[i]]个位置上.原理是:C[A[i]]表示小于等于a[i]的元素个数,正好是A[i]排序后应该在的位置.而且从length[A]到1逆序循环,可以保证相同元素间的相对顺序不变,这也是计数排序

【数据结构】非比较排序算法(实现计数排序和基数排序)

● 计数排序 1.算法思想: 计数排序是直接定址法的变形.通过开辟一定大小的空间,统计相同数据出现的次数,然后回写到原序列中. 2.步骤: 1)找到序列中的最大和最小数据,确定开辟的空间大小. 2)开辟空间,利用开辟的空间存放各数据的个数. 3)将排好序的序列回写到原序列中. 具体实现如下: void CountSort(int *arr, int size) {  assert(arr);  int min = arr[0];  int max = arr[0];  int num = 0;

计数排序(counting_sort) 算法分析

线性时间排序的一种(时间复杂度为(n)) 计数排序(Counting sort)是一种稳定的排序算法.计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数.然后根据数组C来将A中的元素排到正确的位置. 计数排序的特征 当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k).计数排序不是比较排序,排序的速度快于任何比较排序算法. 由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序

计数排序算法

计数排序(Counting sort)是一种稳定的线性时间排序算法.计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数.然后根据数组C来将A中的元素排到正确的位置. 本文地址:http://www.cnblogs.com/archimedes/p/counting-sort-algorithm.html,转载请注明源地址. 计数排序的特征 当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k).计数排序不是比较排序,排序的速度快于任何比较

计数排序C语言实现

计数排序是稳定排序,而且是线性时间内的排序算法,时间复杂度是O(n + k)其中k是待排序数组的范围.计数排序不是原址排序,空间复杂度是O(n + k) //k是指A中的数组范围是0~k 包括0和k void count_sort(int A[], int B[], int n, int k) { int *C = (int *)malloc(sizeof(int) * (k + 1)); int i; for(i = 0; i <= k; i++) { C[i] = 0; } for(i =