TopK疑问(堆排序)-- go
TopK问题(堆排序)
一、堆
1.1 堆的定义
完全二叉树 + 堆序性(指的是大顶堆/小顶堆)
大顶堆:节点本身的值为根的子树/树的各个节点的最大值(递归性理解:只需要节点大于两个子节点)
小顶堆:节点本身的值为根的子树/树的各个节点的最小值(递归性理解:只需要节点小于两个子节点)
1.2堆的存储
层序遍历后,可以放在一维数组/切片中,有切片索引和堆位置索引的规律:
节点索引为 i,其子节点为 2i+1、2i+2
1.3 堆的基操
上滤:节点向上移动(原因是下面的节点破环堆序性)-- 插入场景
下滤:节点向下移动(原因是上面的节点破环堆序性)-- 调整
1.4 建堆
自顶向下:保持顶部堆序性的建堆方法+ 破环底部 + 上滤
自底向上:保持底部堆序性的建堆方法+ 破环顶部 + 下滤(全插入再调整)
1.5 应用
堆排序:本质还是使用优先队列
优先队列:在go中实现heap接口(PList 为 优先队列类型)之后可以使用heap包的相关方法
// 这里采用 int 类型作为元素
// topK问题:采用堆排序的方式(优先队列的数据结构)
// 实现优先队列
type PList []int
// 实现go内置的heap接口
// 修改切片内容,只需要使用值接收者
func (p PList) Len() int{
return len(p)
}
func (p PList) Swap(i,j int){
p[i], p[j] = p[j], p[i]
}
// 大顶堆
func (p PList) Less(i,j int) bool{
return p[i] > p[j]
}
// 修改切片本身,需要使用指针接收者
func (p *PList) Push(x interface{
}){
// 解引用出旧的切片
old := *p
// 类型转换后加入
*p = append(old, x.(int))
}
func (p *PList) Pop() interface{
}{
// 解引用出旧的切片
old := *p
n := len(old)
// 弹出最后一个元素
x := old[n-1]
*p = old[0:n-1]
return x
}
二、TopK问题
面试题 17.14. 最小K个数 - 力扣(LeetCode)
整体思路:创建大顶堆,将大元素全部放到大顶堆中,heap堆
func smallestK(arr []int, k int) []int {
// 边界:
if k <= 0 || k >
len(arr) {
return nil
}
// 创建大顶堆
h := make(PList, k)
// 先放入k个元素
copy(h, arr[:k])
// 初始化为堆结构
heap.Init(&h)
// 遍历剩余元素
for _,num := range arr[k:]{
// 因为是大顶堆,新元素小则需要修复
if num < h[0]{
h[0] = num
// 索引0处的修改,需要修复
heap.Fix(&h, 0)
}
}
// 将堆中元素取出并返回
result := make([]int, k)
for i := 0; i < k; i++ {
result[i] = heap.Pop(&h).(int)
}
return result
}
// topK问题:采用堆排序的方式(优先队列的数据结构)
// 实现优先队列
type PList []int
// 实现go内置的heap接口
// 修改切片内容,只需要使用值接收者
func (p PList) Len() int{
return len(p)
}
func (p PList) Swap(i,j int){
p[i], p[j] = p[j], p[i]
}
// 大顶堆
func (p PList) Less(i,j int) bool{
return p[i] > p[j]
}
// 修改切片本身,需要使用指针接收者
func (p *PList) Push(x interface{
}){
// 解引用出旧的切片
old := *p
// 类型转换后加入
*p = append(old, x.(int))
}
// 大顶堆
func (p *PList) Pop() interface{
}{
// 解引用出旧的切片
old := *p
n := len(old)
// 弹出最后一个元素
x := old[n-1]
*p = old[0:n-1]
return x
}
// 采用小顶堆
func smallestK(arr []int, k int) []int {
if k <= 0 || k >
len(arr) {
return nil
}
h := make(PList, k)
copy(h, arr[:k])
heap.Init(&h)
for _,num := range arr[k:]{
// 小顶堆:只允许大元素加入
if num > h[0]{
h[0] = num
heap.Fix(&h, 0)
}
}
result := make([]int, k)
for i := 0; i < k; i++ {
result[i] = heap.Pop(&h).(int)
}
return result
}
type PList []int
func (p PList) Len() int{
return len(p)
}
func (p PList) Swap(i,j int){
p[i], p[j] = p[j], p[i]
}
// 小顶堆: 小于时返回true
func (p PList) Less(i,j int) bool{
return p[i] < p[j]
}
func (p *PList) Push(x interface{
}){
old := *p
*p = append(old, x.(int))
}
func (p *PList) Pop() interface{
}{
old := *p
n := len(old)
x := old[n-1]
*p = old[0:n-1]
return x
}
浙公网安备 33010602011771号