zoukankan      html  css  js  c++  java
  • 非比较排序算法———计数排序

    计数排序、基数排序、桶排序 非比较排序算法,平均时间复杂度都是O(N).

    这些排序元素,因为其关键值本身就含有了定位特征,因而不需要比较就可以确定其前后位置。

    1、计数排序是一种简单的排序方法,将排序结果放到另一个的新的数组中。

          计数排序要求 待排序的元素的关键值是位于0-k之间的正整数。因而是个非常特殊的情况。

          输入数组A:元素关键值是 0-K的正整数,可以有重复值

          输出数组B:输出数组A的一个非减序列

          中间数组C:大小K,它的i(0<=i<=k)索引位置存储的是A元素集合和。

          这里意思是:原始数组A元素变成了中间数组C下标。       

          
    void Sort(int array[],int n,int outArray[],int k) //默认 区间是0——K
    {
    	int* Temp=new int[k+1];
    	memset(Temp,0,(k+1)*sizeof(int));     
    	for(int i=0;i<n;i++)
    	    Temp[array[i]]++;
    	int m=0;
    	for (int i=0;i<=k;i++)
    	{
    	  while(Temp[i]-->0)//如果有重复的话要递减
              outArray[m++]=i; //array[m++]也是可以的 这里是标准计数排序的优化 可以使outArray 省去
    	}
    	delete[] Temp;
    }
    

      第一次我以为这就是计数排序,但后来发现 这个算法虽然可以解决有限区间的排序问题。但不是标准的(算法导论上)计数排序。

    这个算法严格的来说并非排序算法,像统计(引用别人的描述)。所以也就不是什么稳定排序与非稳定排序,因为排序后的数组中的数已尽被全新的数替代。 已尽改变以前的数组中元素。

    引用:维基百科计数排序

    当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。

    由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序中的算法来排序数据范围很大的数组。

    算法的步骤如下:

    1. 找出待排序的数组中最大和最小的元素
    2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i
    3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
    4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
    void BookSort(int array[],int outarray[],int n,int k)
    {
        //标准排序
        int* Temp=new int[k+1];
        memset(Temp,0,(k+1)*sizeof(int));     
        for(int i=0;i<n;i++)
            Temp[array[i]]++;
        for (int i=1;i<=k;++i)
        {
            Temp[i]=Temp[i]+Temp[i-1];
        }
        //for (int i=n-1;i>=0;--i) //从后往前遍历原始数组 是稳定排序
        for (int i=0;i<n;++i) //从前往后遍历原始数组 就不再是稳定排序
        {
            //计数排序核心思想
            int m=array[i];//原始数组元素关键字的值
            int q=Temp[m];//根据原始数组元素关键字的值 对应于临时数组的下标 找到临时数组的值
            outarray[ q- 1] = array[i];//临时数组的值 即是原始数组原始关键字的 排序位置
            Temp[m] =Temp[m]-1;//因为有重复的数 排序的位置需要减去一个
        }
    }

     代码测试:

    int N=20;
        int array[]={0,0,8,9,6,9,4,0,6,0,0,5,65,76,77,8,79,98,89,96};
        int* outArray=new int[N];
        Sort(array,N,outArray,100);
        //BookSort(array,outArray,N,100);
        for (int i=0;i<N;++i)
        {
            cout<<outArray[i]<<" ";
        }
        system("PAUSE");
        return EXIT_SUCCESS;

     总结:

           计数排序要求规则太多,内存要求很大。接下来我会将其它线性排序算法写一写。然后直接做比较以及和其他非线性排序之间的比较。

    文中肯定存在错误,请您如果发现任何有疑问的地方给我信息。谢谢...

    PS:C++ 知识点

    初始化中间数组C 运用 C++ 函数 void *memset(void *s,int c,size_tn)    memset是对字节进行操作

    char a[5];  memset(a,'1',5); 可以全部初始化为'1'

    int a[5]; memset(a,0,5); 并不能全部初始化为0 因为是按字节进行初始化的 所以应该 memset(a,0,5*sizeof(int));

     

    指针的大小是问:一个指针变量占用多少内存空间?分析:既然指针只是要存储另一个变量的地址,。注意,是存放一变量的地址,而不是存放一个变量本身,所以,不管指针指向什么类型的变量,它的大小总是固定的:只要能放得下一个地址就行!32位系统存放一个地址需要几个字节?答案是和一个 int 类型的大小相同:4字节。

    指向数组的指针: int arr[] = {1,2,3,4,5}; //一个数组

    int* parr; //一个指针。parr = arr; //没有&?对啊,对数组就是不用取址符。

    cout << *parr << endl;  //输出 *parr    正确答案是输出数组中的第一个元素: 1 。

    重点 & 易错点:对指针 进行加1操作,得到的是下一个元素的地址,而不是原有地址值直接加1。知到了如何“加”,也就知道了如何“减”。减以后,得到的是上一个元素的大小。所以,一个类型为 T 的指针的移动,以 sizeof(T) 为移动单位。int* pInt; 移动单位为 sizeof(int) 。即:4。而 char* pChar; 移动单位为 sizeof(char)。即1。

    后置 ++ 或 后置-- 操作,需要系统生成一个临时变量。

     * (作为地址解析符) ++ 同时作用在指针时,不管是前置还是++,都要比*有更高的优先级。

    指针和数组是不一样的,但数组做为参数传递时就会退化为同类型的指针.

    有2个原则:对数组sizeof是数组的大小;对指针sizeof是指针的大小(4);

    学习技术不只是为养家糊口,也为夜深人静的时候能够一个人静静享受这其中的乐趣。
  • 相关阅读:
    关于“每日代码系列”以及后续计划
    每日代码系列(22)
    每日代码系列(21)
    mvcc
    父进程是1号进程产生大量的僵尸进程的解决方案
    nginx学习之路
    Zookeeper Curator 分布式锁
    jvm垃圾收集器汇总
    MySql分库分表以及相关问题
    Https交互原理
  • 原文地址:https://www.cnblogs.com/renxs/p/2657377.html
Copyright © 2011-2022 走看看