非比较排序算法———计数排序

计数排序、基数排序、桶排序 非比较排序算法,平均时间复杂度都是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);

学习技术不只是为养家糊口,也为夜深人静的时候能够一个人静静享受这其中的乐趣。
原文地址:https://www.cnblogs.com/renxs/p/2657377.html