计数排序+基数排序

这几天一直在写排序算法,从插入,冒泡,选择到归并和快速再到计数和基数排序。今天来写下计数排序和基数排序吧。

计数排序:对于一组小于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[i]=0
    int *C=new int[k+1];
    memset(C,0,(k+1)*sizeof(int));
    for(int i=1;i<=n;i++){
        C[A[i]]++;
    }
    for(int i=0;i<=k;i++){
        //cout<<C[i]<<endl;
        while(C[i]--){
            cout<<i<<" ";
        }
    }
    cout<<endl;
    delete C;
}

输入:4 2 5 6 2 6 1 7

输出:1 2 2 4 5 6 6 7

注意看这段代码执行后的结果,我们并没有对数组的元素进行排序,输入的结果却是有序的。怎么会这样呢?我们知道数组的所有值都在[0..k]之间,首先记录了每一值出现的次数,然后在从0–>k扫描,输出某个i存在的个数,就输出几个。其实这就是所为的计数排序的主要代码,我们只需要在将输出的排序值,输入到原数组A中,即可,我们已经知道每个值有几个,当然也就可以累加知道每个值在数组排序后在第几位,我们只是需要用一个辅助数组B暂时储存排序值,在复制到原数组中。

代码:

void CountSort(int *A,int n,int k){//这里的K可以使用A中的最大值
    int *B=new int[n+1];//排好序的数组暂时放到B中,再复制到A中
    int *C=new int[k+1];//同上,C[i]记录每一个i出现的次数,初始为0
    memset(C,0,(k+1)*sizeof(int));

    for(int i=1;i<=n;i++){
        C[A[i]]++;
    }
    for(int j=1;j<=k;j++){
        C[j]=C[j]+C[j-1];//此时,C[j]中用来存放小于j的数的个数,
                         //也就是说C[j]现在表示j所在的有序序列下标
    }

    for(int j=n;j>=1;j--){
        B[C[A[j]]]=A[j];
        C[A[j]]--;//可能会有重复元素,每当将一个值A[j]放入数组,
                  //都要减小C[A[j]]的值,这会使下一个等于A[j]的值,
                  //直接进入数组B中的A[j]的前一个位置
    }
    //Copy :B-->A
    for(int i=1;i<=n;i++){
        A[i]=B[i];
    }
    delete B;
    delete C;
}

测试注代码:

int main()
{
    int A[9]={0,4,2,5,6,2,6,1,7};
    //CountPrint(A,8,10);
    PT(A,8);
    CountSort(A,8,10);
    PT(A,8);
    return 0;
}

基数排序:

*基数排序,对于给定的n个d位数进行的排序,

*当然我们可以对任意数排序,不足d位前面补0即可

*什么是基数排序,我们不用担心,下面来看这个例子进行了

*329 720

*457 355

*657 436

*839–按每个数的个位数进行排序->457—->

*436 657

*720 329

*355 839

*

*720 720

*355 329

*436 436

*457–按每个数的十位数进行排序->839—>

*657 355

*329 457

*839 657

*

*720 329

*329 355

*436 436

*839–按每个数的百位数进行排序->457—>

*355 657

*457 720

*657 839

*

*进过3次对每一位数上的数字进行排序,已完成的排序

*这就是基数排序,通过对关键字的基数排序,达到最终排序

*读者可能会问可以从高位到低位进行排序吗?不可以!!

*对于一个数而言,其权位越高对这个数的大小影响越大,

*因此当低位进行排序后,通过高位的排序改变低位排序的

*结果,是合理的,然而如果是通过低位排序改变高位的排序结果

*是不合理的。

*注意:对每一位排序的时候,所有值都在0~9之间,我们可以用到

*计数排序(CountSort)的方法

计数排序辅助排序的代码函数,类似计数排序,也稍有不同,

代码:

void CountSort(int *A,int *T,int n){
    int *B=new int[n+1];//排好序的数组暂时放到B中,再复制到A中
    int *C=new int[10];//同上,C[i]记录每一个i出现的次数,初始为0
    memset(C,0,(10)*sizeof(int));

    for(int i=1;i<=n;i++){
        C[T[i]]++;
    }
    for(int j=1;j<=9;j++){
        C[j]=C[j]+C[j-1];//此时,C[j]中用来存放小于j的数的个数,
                         //也就是说C[j]现在表示j所在的有序序列下标
    }

    for(int j=n;j>=1;j--){
        B[C[T[j]]]=A[j];
        C[T[j]]--;//通过T对A排序,可能会有重复元素,每当将一个值A[j]放入数组,
                  //都要减小C[T[j]]的值,这会使下一个等于A[j]的值,
                  //直接进入数组B中的A[j]的前一个位置
    }
    //Copy :B-->A
    for(int i=1;i<=n;i++){
        A[i]=B[i];
    }
    delete B;
    delete C;

}

基数排序主代码:

/**
 *调用RadixSort(A,n):A[1..n]
 *n个d为数进行排序
 */
void RadixSort(int *A,int n){//
    //为了方便取到每一位的数字,将A[i]写到字符串
    int d=0,x=A[1];
    while(x){//计算每个数的位数d
        d++;
        x/=10;
    }
    //cout<<d<<endl;

    //申请二维数组空间
    char **S=new char*[n+1];
    for(int i=0;i<=n;i++){
        S[i]=new char[d];
    }
    for(int i=1;i<=n;i++){
        sprintf(S[i],"%d",A[i]);
        //printf("%s\n",S[i]);
    }
    --d;
    int *T=new int[n+1];//存储相同位权上的每一位数
    while(d!=-1){//低位到高位模拟排序
        //对[0..9],调用计数排序
        for(int i=1;i<=n;i++){
            T[i]=S[i][d]-‘0‘;
        }
        CountSort(A,T,n);//通过B的顺序对A排序
        for(int i=1;i<=n;i++){//重新将排序的A写入字符串,
                              //因为计数排序与位置有关
            sprintf(S[i],"%d",A[i]);
            //printf("%s\n",S[i]);
        }
        --d;
    }
    delete T;
    for(int i=0;i<=n;i++){
        delete S[i];
    }
    delete S;
}

测试主函数:

int main()
{
    int A[8]={0,329,457,657,839,436,720,355};
    PT(A,7);
    RadixSort(A,7);
    PT(A,7);

    return 0;
}

注:

#define PT(A,n) for(int i=1;i<=n;i++) cout<<A[i]<<" "; cout<<endl;
时间: 2024-11-08 21:37:38

计数排序+基数排序的相关文章

常见的排序算法(四)( 归并排序,计数排序 , 基数排序)

 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序列:即先使每个子序列有序,再使子序列段间有序.若将两个有序表合并成一个有序表,称为二路归并. (如果读者不太了解什么叫分治法,可以去看看<算法导论>第一二章.) 归并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1:否则将第

基数排序与桶排序,计数排序【详解】

桶排序简单入门篇^-^ 在我们生活的这个世界中到处都是被排序过的东东.站队的时候会按照身高排序,考试的名次需要按照分数排序,网上购物的时候会按照价格排序,电子邮箱中的邮件按照时间排序……总之很多东东都需要排序,可以说排序是无处不在.现在我们举个具体的例子来介绍一下排序算法. 首先出场的是我们的主人公小哼,上面这个可爱的娃就是啦.期末考试完了老师要将同学们的分数按照从高到低排序.小哼的班上只有5个同学,这5个同学分别考了5分.3分.5分.2分和8分,哎,考得真是惨不忍睹(满分是10分).接下来将分

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

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

有Leetcode中一道题,谈桶排序,基数排序和计数排序

几种非比较排序 在LeetCode中有个题目叫Maximum Gap,是求一个非排序的正数数列中按顺序排列后的最大间隔.这个题用桶排序和基数排序都可以实现.下面说一下桶排序.基数排序和计数排序这三种非比较排序. 桶排序 这种排序的主要思想是,把数列分配到多个桶中,然后再在各个桶中使用排序算法进行排序,当然也可以继续使用桶排序. 假设数组的最大值是A,最小值是B,长度是L,则每个桶的大小可以是S=Max(1,(A-B)/(L-1))则可以分为(A-B)/S+1个桶. 对于数列中的数字x,用(x-B

常见排序算法之计数排序与基数排序

1.计数排序 顾名思义,是对待排序数组中的数据进行统计,然后根据统计的数据进行排序,例如: 待排序数组: a[] = { 100, 123, 112, 123, 201, 123, 112, 156, 156, 178, 185, 156, 123, 112 } 首先取出数组中最大值与最小值(这个不用说了吧) 最大值:201  最小值:100 因此该数组的数值范围为 201 - 100 + 1 = 102 开辟一个大小为102的数组 count[102] 并将所有元素初始化为0 再次遍历数组,以

【排序】基数排序(计数排序、桶排序)

在此对于桶排序做出两种方法: 一.简化版桶排序 代码如下: <span style="font-size:18px;">/*简化版的桶排序*/ #include <stdio.h> int main() { int book[1001],i,j,t,n; for(i=0;i<=1000;i++) { book[i]=0; } scanf("%d",&n);//输入一个数n,表示接下来有n个数 for(i=1;i<=n;i+

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

计数排序 计数排序的原理 设被排序的数组为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.计数排序 找出待排序的数组中最大和最小的元素 统计数组中每个值为i的元素出现的次数,存入数组C的第i项 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加) 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1 void CountSort(int *a, int size) { assert(a); int max = a[0]; int min = a[0]; for (int i = 1; i < siz

线性排序:计数排序与基数排序

前面讨论了几种排序算法,三种O(n2)时间复杂度的排序算法:插入,选择和冒泡和两种O(nlgn)的算法:快速排序和归并排序.这几种排序数组除了归并排序需要额外的数组开销.其他几个的空间复杂度都是O(1).通过比较交换元素完成排序. 计数排序是利用空间换取时间,增加了两个额外数组的开销,而且计数排序有一定限制:即待排数组元素的大小在0-P之间.如果P的值很大,那么额外数组的开销也会很大. 计数排序,顾名思义就是通过一个额外数组来记下待排序数组中元素出现的次数. void countsort(int