首先介绍一下桶思想:
在现实世界中,大部分的数据分布是均匀的,或者在设计的时候让它可以均匀分布,或者说可以转换为均匀的分布。
数据均匀分布了,桶排序的效率就能发挥出来。(分库分表)
误区: 1.如果数据分布不均匀,大量的数据集中在少数桶里,桶排序就没效果了。
2、桶排序要时间就省不了空间,要空间就省不了时间。结论是桶排序意义不大。
桶排序或所谓的箱排序
桶排序(Bucket sort)的原理:
假如输入的数据服从均匀分布,将数据分到有限数量的桶里,然后对每个桶分别进行排序,最后把 桶的数据全部合并。
桶排序的时间复杂度,取决于对各桶之间数据进行排序的时间复杂度,因为其他部分的时间复杂度都为O(n)。显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。
例子力扣算法题目:topK元素(前K个高频元素)要求时间复杂度优于O(nlogn)。
解题思路:
1、使用字典统计数组中每个元素出现的频率,key=元素,value=元素出现的频率
2、使用一维数组构造桶,桶的数目小于等于数组中元素的个数,将频率作为一维数组下标,对于出现不同频率的元素集合,存入对应的一维数组下标,桶的长度取数组长度+1;
3、倒序遍历一维数组,获取出现频率送达到小的排列, result中的元素超过k退出遍历即可
func topKElem(nums []int, k int) (result []int) {
/*字典统计数组中每个元素出现的频率*/
frequence := map[int]int{}
for _, num := range nums {
if _, ok := frequence[num]; ok {
frequence[num] += 1
} else {
frequence[num] = 1
}
}
/*将频率作为一维数组下标,对于出现不同频率的元素集合,存入对应的一维数组下标*/
freqArr := make([]int, len(nums)+1)
for elem, freq := range frequence {
freqArr[freq] = elem
}
/* 倒序遍历一维数组,获取出现频率送达到小的排列, result中的元素超过k退出遍历即可*/
for i := len(freqArr) - 1; i >= 0; i-- {
if freqArr[i] != 0 && len(result) < k {
result = append(result, freqArr[i])
}
}
return result
}
func topKElem(nums []int, k int) (topKIds []int) {
/*字典统计数组中每个元素出现的频率*/
frequence := map[int]int{}
for _, num := range nums {
if _, ok := frequence[num]; ok {
frequence[num] += 1
} else {
frequence[num] = 1
}
}
res := make([][]int, len(nums)+1)
maxFreq := 0
for id, freq := range frequence {
if freq > maxFreq {
maxFreq = freq
}
if res[freq] != nil {
res[freq] = append(res[freq], id)
} else {
res[freq] = []int{id}
}
}
for i := maxFreq; len(topKIds) < k; i-- {
for j := 0; j < len(res[i]); j++ {
topKIds = append(topKIds, res[i][j])
if len(topKIds) >= k {
break
}
}
}
return
}
测试结果:
func main(){
nums := []int{1, 1, 6}
fmt.Println(topKElem(nums, 2))
}
>>[1 6]