用排序来直击信息的本质
前言:
计数排序,顾名思义它是统计每个元素出现的次数。实际上是通过限定了信息的范围,在只适用于整数情况下达到了线性时间性能。(整数集合在已知范围的情况下,整数的个数是有限的,实际上限定数据为整数,是把问题从无限多种情况,变成了有限种情况)
算法原理:
1. 先计算数据中的最大值max最小值min。(找出数据范围)
2.开辟一个大小为M(max-min+1)存储空间数组 sortArr。(开辟存储空间)
3.遍历每一个元素arr[i],对其执行 sortArr[hashKey(arr[i],min)]++。(开始计数)
设:
数据数需要处理的数据为arr, 长度为len,countingSort为排序入口函数,hashKey为映射函数
伪码:
getMaxMin(arr,len)
{
max = arr[0];
min = arr[0];
for(i=1;i<len;i++)
{
if(arr[i]>max)
{
max = arr[i];
}
else if(arr[i]<min)
{
min= arr[i];
}
}
return max,min;
}
hashKey(key,min)
{
return key- min;
}
countingSort(arr,len)
{
max,min = getMaxMin(arr,len);
m = max - min+1;
sortArr = size(m);
for(i=0;i<len;i++)
{
sortArr[hashKey(arr[i],min)]++;
}
}
算法图:
获取最大最小值:
统计元素数量:
算法性能:
1.计算最大值max和最小指min需要遍历整个数组,性能消耗为len。
2.开辟一个大小为M的存储空间,性能消耗为M。
3.遍历每一个数据进行计数,性能消耗为len。
4.那么性能为2*len+M,所以时间复杂度为O(n+m)。(计算性能时,我们不计算系数项,n为len,m为M)
结语:
1.在计数排序中,数据一定为整数,事实上是对信息进行了分类,对问题进行了降维操作,在一般排序问题中处理的数据为浮点数据,本质是对连续型数据进行处理,而只处理整数后,问题变成了在离散型数据上的处理。(在连续数据向离散数据进行转换时会简化问题,同时会带来信息上的丢失,丢失的信息其实就是被简化的部分。在数学上微积分、线性代数、拟合等其实就是解决这些问题的方式,在这不过多的讨论这个问题。 在日常生活中,离散数据是我们常用且重要解决问题的手段,在计算机中非常常见,一个典型的代表就是绘制一根斜线,计算机在绘制一个线段的时候是一个个的像素点进行绘制的)
2.实际上计数排序还有一个隐性的要求,也就是max-min不能太大,不然会造成大量空间浪费,甚至会让算法变得不可行,因为实际应用中我们的可使用空间实际上是有限制的,不可能无限大。
3. 在计数排序中映射函数hashKey是一个非常简单的散列函数,在max-min的大小能接受的情况下,可以直接使用以上代码处理,但实际上如果max-min大小不能接受的情况下,可以使用字典的散列函数设计解决这个问题。(在字典这种数据结构中,key的寻址所使用的散列函数,是解决这个问题的方式之一。这里不做过多的深入,这里只单纯给出解决方案,当然这个方案会降低算法的性能)
4.在排序元素不多,但元素值相差巨大的时,会导致性能低于传统比较排序。(当M>nlog2(n)的时候,计数排序的性能是差于快速排序等比较排序算法的)
5.对于有"最小单位"的浮点数据实际上也是可以使用计数排序的,例如在统计学生成绩的时候,我们能碰到的最小单位为0.5,实际上我们对所有的元素数据进行*2的操作后可以继续使用计数排序,但是这里的实质是把浮点数据映射到整数中去(对问题进行降维操作,把连续型问题转换成离散型问题),但在绝大部分情况下,是无法这么做的。
6.在计数排序中我们使用了空间换时间的思想,但是空间换时间的前提是让数据“降维”,只有这样我们才能达到目的,实际上这就是对信息的一种假设(不会出现浮点数据)。
ps:数据为整数时看到一个线性时间的算法,相较于比较排序(快速排序)算法进行了指数级别的优化,同时也有疑问是不是存在一个算法,在一般浮点数下,也可以达到线性时间,答案是肯定的,此算法就是:桶排序。