刷到了一道leetcode算法题,虽然不难,但是却让我寸步难行,因为在此之前,我从未了解过计数排序和桶排序。
1. 题目分析
这道题看上去不难,但是若不了解计数排序和桶排序的概念,会绕很大的圈子,那我们现在来看一下什么是计数排序和桶排序。
2. 计数排序
目前我们所了解的最快的排序就是快速排序,但是有没有一些特定的情况,一些排序方式的速度超过快速排序呢?
1. 什么是计数排序?
计数排序是一种基于下标的排序,给出图解
- 假定20个随机整数的值如下:
9,3,5,4,9,1,2,7,8,1,3,6,5,3,4,0,10,9 ,7,9 - 遍历这个数组
比如第一个整数是9,那么数组下标为9的元素加1:
第二个整数是3,那么数组下标为3的元素加1:
继续遍历数列并修改数组…
最终,数列遍历完毕时,数组的状态如下:
数组每一个下标位置的值,代表了数列中对应整数出现的次数。
有了这个“统计结果”,排序就很简单了。直接遍历数组,输出数组元素的下标值,元素的值是几,就输出几次:
0,1,1,2,3,3,3,4,4,5,5,6,7,7,8,9,9,9,9,10
显然,这个输出的数列已经是有序的了。
2. 计数排序适用范围
当一个数组里的数全是整数,并且跨度不大,举几个例子。
- [2,3,6,9,8,7,4,5,6] 这个数组中值的范围是2~9,可以使用计数排序
- [2,100000,3,20000000] 这个数组中的范围太大了,不能使用计数排序
- [2,1.1,2.2] 存在小数,不能使用计数排序
3. 桶排序
1. 什么是桶排序?
顾名思义,就是一个一个的桶构成的排序,计数排序的弊端很大,桶排序是类似于计数排序的一种排序方式,不过桶排序的使用范围更广。
给出图解
每一个桶(bucket)代表一个区间范围,里面可以承载一个或多个元素。桶排序的第一步,就是创建这些桶,确定每一个桶的区间范围:
第二步,遍历原始数列,把元素对号入座放入各个桶中:
第三步,每个桶内部的元素分别排序(显然,只有第一个桶需要排序):
将桶里面的数据拿出来煤科院发现,已经是有序的了。
4. 代码实现
这道题我们用计数排序
1. Python代码
class Solution(object):
def frequencySort(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
arr = [0 for _ in range(201)]
res = [0 for _ in range(len(nums))]
max1 = 0
index = 0
res_index = len(res) - 1
for i in nums:
arr[i + 100] += 1
while True:
max1 = 0
index = 0
for i in range(201):
if arr[i] > max1:
max1 = arr[i]
index = i
arr[index] = 0
if max1 == 0:
break
for i in range(max1):
res[res_index] = index - 100
res_index -= 1
return res
2. Java代码
class Solution {
public int[] frequencySort(int[] nums) {
int[] arr = new int[201];
int[] res = new int[nums.length];
int max = 0;
int index = 0;
for(int i:nums){
arr[i + 100] += 1;
}
for(int j = res.length - 1;j >= 0;j--){
max = 0;
index = 0;
for(int i = 0;i < arr.length;i++){
if(arr[i] > max){
max = arr[i];
index = i;
}
}
arr[index] = 0;
if(max == 0){
break;
}
for(int k = 0;k < max;k++){
res[j] = index - 100;
j--;
}
j++;
}
return res;
}
}