线性排序就是可以在O(n)时间复杂度内完成的排序。
常见的排序方式有:桶排序,计数排序,基数排序。
之所以能做到线性时间内排序,是因为这三种排序方式不是基于比较的排序算法,而且他们对数据要求比较苛刻
-
桶排序
桶排序顾明思议就是将数据分几个有序的桶里,然后每个桶内单独进行排序。桶内排完序之后,再按顺序把每个桶内的数据依次取出,组成的序列就是有序的了。
我们根据下面这张图来介绍一下桶排序
将数据分好桶以后,桶内数据量比较小,就可以用快速排序进行排序。
所以这里最关键的就是要保证分组的均匀性,如果所有数据都分到了一个桶里,那么这里就又退化成了快排了。 计数排序
计数排序 其实就是一个特殊的桶排序。即当排序的数据范围不是很大的时候,每个数据自成一个桶,每个桶内的数据都相同,这样就省去了桶内排序。
假如我们要给数组S{2,5,3,0,2,3,0,3} 排序,该数组内的数据都小于5,那么我们就可以用计数排序来对它进行排序。我们可以用一个大小为6的数组来存储各个各个数字出现的次数,可以得到数组R {2,0,2,3,0,1},即0出现了2次,1出现了0次,2出现了2次,3出现了3次,4出现了0次,5出现了1次。
如果只是对单纯的数字进行排序,那么我们通过下面的代码就可以得到一个排序后的结果了
int index = 0;
for(int i = 0; i < R.length; i++){
while(R[i] > 0){
result[index++] = i;
R[i] = R[i] - 1;
}
}
但其实好多时候我们可能是按照结构体中的某一个属性进行的计数排序,这样就不能只通过遍历计数结果数据进行排序了。
下面我们来看另一种解法:
上面我们统计出每个数字出现的次数之后,还可以进一步处理,将每个元素和它之前出现的数字次数求和,这样每个数字就表示小于等于它的数字的出现的次数,R{2,2,4,7,7,8}。这样当我们拿到数据3的时候,我们就可以知道,小于等于3的数字有7个,因此3应该放在索引值为6的位置,同时将7改为6,表示小于等于3的数字还有6个,当再遇到3时,其位置应该是索引值为5的位置,以此类推我们就可以将各个数字放到它对应的位置上了。因为这种方法需要用到以前的数据,所有需要O(n)的空间复杂度。
java代码实现如下
int[] result = new int[S.length];
for(int i = 1; i < R.length; i++){
R[i] += R[i-1];
}
for(int i = 0; i < S.length; i++){
result[R[S[i] - 1] = S[i];
R[S[i]] -= 1;
}
-
基数排序
基数排序适合于数值为数较多,如手机号,单词这种数据的排序。它的基本思想是,将单词或者手机号每一位都拆开,然后从后向前分别对每一位进行稳定排序,最后得到结果。
下图就是一个很好的例子。