go实现利用最大堆寻找最小k个数

本文介绍了如何使用Go语言实现最大堆来寻找数组中最小的k个数,通过建立容量为K的最大堆,以O(nlogk)的时间复杂度解决这个问题。文章详细解析了堆的原理、思路设计,并提供了代码分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

不忘初心,砥砺前行 

作者 | 陌无崖

转载请联系授权 

导语

昨天分享了寻找最小k个数的算法是Go寻找数组中最小的k个数——全部排序和部分排序,那么有没有更为迅速的方法呢?今天就来分享关于如何使用最大堆进行解决。

什么是堆

我太懒了,直接上我画好的思维导图吧哈哈,获取高清的也可以关注我的公众号,后台回复【堆】

思路设计

知道了如上定义,我们就可以将容量为K的最大堆存储我们的最小k个数,因此我们仍然可以按照之前的方法假设堆中存储的仍然是最小的k个数(不懂的可以看我的上一篇文章),再通过比较替换或不替换堆来最终找到我们的最小的k个数。此解法与解法二类似但由于使用堆时进行查找或更新的时间复杂度降低到了O(logk),那么通过遍历剩余的n-k个数,那么最坏情况下的时间复杂度为O(k + (n-k)logk) = O(nlogk)。这里的O(k)为建堆时的时间复杂度。

因此我们需要建堆,通过以上的分析我们可以用一维数组存储我们的堆,我们先来看一个完全二叉树找一下规律

我们将1,2,3,4,5分别作为下标,在上个图片的思路中,我们可以发现每次我们的遍历刚开始指向的是一个由子节点的父节点,在下图中可知子节点(i)和父节点(l)之间的关系是2 * i + 1 = l,且在同一深度相邻节点相差为1,由上图可知,我们在遍历父节点比较时,依次会经过5,4,3,2,1,0这些下标指向的数值。因此我们可以依次为循环,每次循环的操作都是父节点和子节点进行比较。

代码分析

(1) 循环每一个父节点

(2) 在子节点中找到最大值和父节点比较,若子节点大,则替换

(3) 每次提换后需要记录新的父节点,重新和子节点比较,替换,如下标为2和5的进行替换后,还要保证下标5(原来的下标2)是否满足最大堆性质。因为是同样的操作,因此该过程仍然是一个循环,循环结束的标志是最后一层的叶节点。

完整代码

func BuildMaxHeap(data []int) {
  for i := len(data)/2 - 1; i >= 0; i-- {
    adjustDown(data, i)
  }
}
func adjustDown(data []int, i int) {


  for largest := 2*i + 1; largest < len(data); largest = 2*largest + 1 {
    if largest == len(data)-1 {
      if data[largest] > data[i] {
        // fmt.Println(data[largest], "和", data[i], "进行交换")
        data[largest], data[i] = data[i], data[largest]
        i = largest
      } else {
        // fmt.Println(data[largest], "不和", data[i], "进行交换")
      }
    } else {
      if data[largest+1] > data[largest] && data[largest+1] > data[i] {
        // fmt.Println(data[largest+1], "和", data[i], "进行交换")
        data[largest+1], data[i] = data[i], data[largest+1]
        i = largest
      } else if data[largest+1] < data[largest] && data[largest] > data[i] {
        // fmt.Println(data[largest], "和", data[i], "进行交换")
        data[largest], data[i] = data[i], data[largest]
        i = largest
      } else {
        // fmt.Println(data[largest], "或", data[largest+1], "不和", data[i], "进行交换")
      }
    }
  }
}


// 维护最大堆
func topK(data []int, k int) {
  // 建立前K个数的最大堆
  BuildMaxHeap(data[0:k])
  for i := k; i < len(data); i++ {
    if data[i] < data[0] { //如果剩余的数中有小的数则替换
      data[i], data[0] = data[0], data[i]
      adjustDown(data[0:k], 0)
    }
  }
}

查看完整源码可以点击阅读原文进入github仓库,如果喜欢,感谢你为我点一个星星^_^


END

今日推荐阅读

RabbitMQ系列笔记广播模式和路由模式 
RabbitMQ系列笔记入门篇

RabbitMQ系列笔记work模式

RabbitMQ系列笔记work模式

protoc语法详解及结合grpc定义服务

Golang中Model的使用

基于Nginx和Consul构建高可用及自动发现的Docker服务架构

▼关注我,一起成长

    主要分享 学习心得、笔记、随笔▼

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值