计数排序_COUNTINGSORT

  • 计数排序_COUNTINGSORT

    • 计数排序_COUNTINGSORT分析
    • 代码分析

计数排序_COUNTINGSORT

我们之前讲过四种基本的排序方法:

《快速排序_QUICKSORT》:http://blog.csdn.net/ii1245712564/article/details/45749061

《堆排序_HEAPOSRT》http://blog.csdn.net/ii1245712564/article/details/45534625

《归并排序_MERGESORT》:http://blog.csdn.net/ii1245712564/article/details/42173549

《插入排序_INSERTSORT》:http://blog.csdn.net/ii1245712564/article/details/42079225

上面的四种排序方法都有一个共同点,就是他们的排序都是基于决策树的。也就是说,他们排序的一句就是通过在元素与元素之间的比较进行的。这种排序的最坏情况下运行时间下界至少为Ω(nlogn)。上面的排序算法中,堆排序归并排序的最坏情况y运行时间为Ω(nlogn),而快速排序插入排序的最坏运行时间为O(n2)

引理:任何排序算法都需要做Ω(nlogn)次比较

证明:因为决策树是一棵完全二叉树,即每一个非叶子节点都有两个孩子节点。我们在这里假设我们输入n个元素的数组,决策树的深度为h,于是我们可以得到下面的不等式

n!h≤2h于是两边同时取对数≥log(n!)=Ω(nlogn)

所以任何一种基于决策树的排序算法最坏情况(深度最深)运行时间的下界都为Ω(nlogn)

计数排序_COUNTINGSORT分析

上面我们讲过,任何基于决策树的排序算法最坏情况下的运行时间为Ω(nlogn),决策树的核心操作就是比较数组中的两个元素。那有没有一种方法在不比较两个任何两个元素的情况下对数组进行排序呢?

假设现在输入一个规模为n的输入A={a1,a2,...,an},数组中的每一个元素都满足0≤ai≤k(1≤i≤n), 其中k=O(n)。相信你也看到了,上面的数组有一个限制,就是数组里面的每一个元素都在一个同一个区间范围之内.说明数组的里面的元素am大小是有限的,于是我们创建一个数组countArray[1...k]来来保存数组A里面每一个元素出现的次数countArray[am]表示元素am出现的次数,新建一个数组outArray[1...n]来保存排序结果。

下面是C++代码实现:

/*************************************************
* @Filename:    countingSort.cc
* @Author:      qeesung
* @Email:       [email protected]
* @DateTime:    2015-05-16 13:26:21
* @Version:     1.0
* @Description: 计数排序
**************************************************/
#include <iostream>
#include <ctime>
#include <cstdlib>

using namespace std;

/**
 * 计数排序
 * @param inArray  输入的数组
 * @param outArray 输出的数组
 * @param minEle 数组里面的最小元素
 * @param maxEle 数组里面的最大元素
 * @param arraySize 数组元素个数
 */
void countingSort(int inArray[] ,int outArray[], int minEle ,
                  int maxEle , int arraySize )
{
    if(inArray == NULL ||       outArray == NULL ||       maxEle < minEle ||       arraySize < 0)
        return;
    // 新建一个缓存数组来保存比一个inArray[i]元素大的个数
    const int & countArraySize = maxEle - minEle+1;
    int countArray[countArraySize];
    // 初始化count数组
    for (int i = 0; i < countArraySize; ++i)
    {
        countArray[i] = 0;
    }
    // 遍历一遍inArray,统计各个元素的数量
    for (int i = 0; i < arraySize; ++i)
    {
        countArray[inArray[i]-minEle]++;
    }
    // 现在使得计算比inArray[i]小的元素的个数
    for (int i = 1; i < countArraySize; ++i)
    {
        countArray[i] = countArray[i] + countArray[i-1];
    }

    // 再次遍历一遍inArray,将元素放入outArray合适的位置
    for (int i = arraySize-1; i >= 0; --i)
    {
        outArray[countArray[inArray[i]-minEle]-1] = inArray[i];
        countArray[inArray[i]-minEle]--;
    }
}

void printArray(int array[] , int arraySize)
{
    if(array == NULL)
        return;
    for (int i = 0; i < arraySize; ++i)
    {
        cout<<array[i]<<"\t";
    }
    cout<<endl;
}

int main(int argc, char const *argv[])
{
    const int & arraySize = 10;
    int inArray[arraySize];
    srand((int)(time(NULL)));
    for (int i = 0; i < arraySize; ++i)
    {
        inArray[i] = rand()%9;
    }
    cout<<"before sort array :"<<endl;
    printArray(inArray , arraySize);

    int outArray[arraySize];
    countingSort(inArray , outArray , 0 ,9 , arraySize);
    cout<<"after sort array:"<<endl;
    printArray(outArray , arraySize);
    while(1);
    return 0;
}

运行结果:

before sort array :

4 7 4 5 2 4 6 0 0 2

after sort array :

0 0 2 2 4 4 4 5 6 7


代码分析

代码分析:

countingSort函数里面有四个for循环

for循环1

   // 初始化count数组
  for (int i = 0; i < countArraySize; ++i)
    {
        countArray[i] = 0;
    }

这里的运行时间为O(countArraySize),也就是输入区间的宽度

for循环2

   // 遍历一遍inArray,统计各个元素的数量
    for (int i = 0; i < arraySize; ++i)
    {
        countArray[inArray[i]-minEle]++;
    }

这里遍历一边输入数组,统计输入数组里面的每一个元素的个数,运行时间为O(n)

for循环3

   // 现在使得计算比inArray[i]小的元素的个数
    for (int i = 1; i < countArraySize; ++i)
    {
        countArray[i] = countArray[i] + countArray[i-1];
    }

这个循环做了一个非常精妙的事情,上一个循环里面我们统计出每一个元素的个数,这个循环我们统计数组里面比某个元素小的元素的个数,所以运行时间为O(countArraySize)

for循环4

   // 再次遍历一遍inArray,将元素放入outArray合适的位置
    for (int i = arraySize-1; i >= 0; --i)
    {
        outArray[countArray[inArray[i]-minEle]-1] = inArray[i];
        countArray[inArray[i]-minEle]--;
    }

这个循环里面有一个需要注意的地方,那就是循环变量i是从0arrySize,还是从arraySize0,如果我们从arraySize0,那么排序算法是稳定的,如果从0arrySize,那么排序算法是不稳定的。这里一定要注意。在这个循环里面做了两件事:

- 将inArray[i]元素放入到outArray合适的位置,我们知道比inArray[i]小的元素个数为countArray[inArray[i]-minEle],说明我们的元素应该放在outArraycountArray[inArray[i]-minEle]-1(数组应该从0开始,所以需要减去1)上.因为countArray[inArray[i]-minEle]-1位置之间的那些位置需要放比inArray[i]小的那些元素啊

- 将countArray[inArray[i]-minEle]--,这样是为了避免存在相同的元素,因为我们再统计比自己小的元素的个数的时候是这样做的countArray[i] = countArray[i] + countArray[i-1],我们加上自己的countArray[i]的大小,说明在区间[ countArray[i-1] , countArray[i] ]之间存放的是所有相同的元素inArray[i]

这个循环的运行时间为O(n)

通过上面的分析,我们知道计数排序的运行时间为:

O(countArraySize)+O(n)+O(countArraySize)+O(n)=O(countArraySize+n)

因为我们之前设定为k=O(n),这个设定是必须的,如果我们两个元素,a1=0,a2=10000,如果再按照上面的做法来做,那就得不偿失了。

所以计数排序的运行时间为O(n)

时间: 2024-10-12 08:15:10

计数排序_COUNTINGSORT的相关文章

排序之计数排序

计数排序并非一种基于比较进行的排序,它是计算一个序列中的值在正常排好序中的序列所处的位置,怎么求解一个数的位置呢?就是利用下脚标进行求解,新建一个数组resu[],数组的长度要比序列中的最大值大1,数组中的值全部初始化为0,然后遍历原序列,将原序列的值i作为新建数组resu[]的下脚表,对resu[i]++操作,这样就得出值 i 出现的次数:然后再利用resu[i]=resu[i]+resu[i-1]求解出i在原序列中的位置. 计数排序需要两个额外的数组作辅助,提供临时存储的resu数组,存放最

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

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

计数排序

算法思想 编辑 计数排序对输入的数据有附加的限制条件: 1.输入的线性表的元素属于有限偏序集S: 2.设输入的线性表的长度为n,|S|=k(表示集合S中元素的总数目为k),则k=O(n). 在这两个条件下,计数排序的复杂性为O(n). 计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定).一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上.例如,如果输入序列中只有17个元素的

计数排序Counting sort

注意与基数排序区分,这是两个不同的排序 计数排序的过程类似小学选班干部的过程,如某某人10票,作者9票,那某某人是班长,作者是副班长 大体分两部分,第一部分是拉选票和投票,第二部分是根据你的票数入桶 看下具体的过程,一共需要三个数组,分别是待排数组,票箱数组,和桶数组 var unsorted = new int[] { 6, 2, 4, 1, 5, 9 };  //待排数组 var ballot = new int[unsorted.Length];          //票箱数组 var b

计数排序(Count Sort )与插入排序(Insert Sort)

计数排序法:计数数组适用于当前数组密集的情况.例如(2,3,5,4,2,3,3,2,5,4) 方法:先找出最大值最小值,之后统计每个数出现的次数,根据次数从小到大往数组里添加 计数排序法是一种不需要比较的排序方法 1 void count(int top,int length,int arr[]) 2 { 3 int min=arr[0],max=arr[0],i=1,j=0; 4 int *count=(int*)malloc(sizeof(int)*(max-min+1)); 5 if(ar

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

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

Java实现计数排序

详细讲解见<算法导论>8.2节--计数排序. Java代码如下: package linetimesort; /** * 计数排序假设n个输入元素中的每一个都是介于0到k之间的某个整数,k为某个整数:它 * 通过确定小于等于一个数的数的个数来确定这个数应该放在哪个位置 * @author yuncong * */ public class CountSort { /** * * @param a 排序前的数组 * @param b 排序后的数组 * @param k 排序数组中最大元素的值 *

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

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

计数排序及其扩展思路

(1)原理和代码以及时间复杂度分析 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逆序循环,可以保证相同元素间