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
  }
posted @ 2025-08-25 11:37  yjbjingcha  阅读(12)  评论(0)    收藏  举报