GO--堆(have TODO)

堆(Heap)是一种特殊的数据结构。它是一棵完全二叉树(完全二叉树是指除了最后一层外,每一层上的节点数都是满的,并且最后一层的节点都集中在左边),结放在数组(切片)中,通常分为最大堆(Max Heap)和最小堆(Min Heap)两种类型。

  • 最大堆:在最大堆中,对于每个非叶子节点,它的值都大于或等于其左右子节点的值。也就是说,根节点的值是整个堆中的最大值。例如,对于节点i,如果它有左子节点2i + 1和右子节点2i+2(这里假设节点编号从 0 开始),那么heap[i]>=heap[2i + 1]heap[i]>=heap[2i+2]
  • 最小堆:与最大堆相反,在最小堆中,对于每个非叶子节点,它的值都小于或等于其左右子节点的值。根节点的值是整个堆中的最小值。即对于节点iheap[i]<=heap[2i + 1]heap[i]<=heap[2i+2]

堆的实现

1、使用container/heap包

heap源码中定义了一个Interface 的接口,此接口一共包含五个方法,我们定义一个实现此接口的类就实现了一个二叉堆

package mainimport ("container/heap""fmt"
)type MaxHeap []intfunc (m MaxHeap) Len() int {return len(m)
}func (m MaxHeap) Less(i, j int) bool {//建立大根堆,使用>return m[i] > m[j]
}func (m *MaxHeap) Swap(i, j int) {(*m)[i], (*m)[j] = (*m)[j], (*m)[i]
}func (m *MaxHeap) Push(x any) {*m = append(*m, x.(int))
}func (m *MaxHeap) Pop() any {//拿到堆顶元素res := (*m)[len(*m)-1]//删除堆顶元素*m = (*m)[:len(*m)-1]return res
}func main() {h := make(MaxHeap, 0)//结构体实现了接口中的全部方法后,结构体也就是这个接口类型了,因此我们可以传入hheap.Init(&h)heap.Push(&h, 2)heap.Push(&h, 1)heap.Push(&h, 3)fmt.Println(heap.Pop(&h))fmt.Println(heap.Pop(&h))fmt.Println(heap.Pop(&h))}

下面我们一起去看看源码:

 

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.// Package heap provides heap operations for any type that implements
// heap.Interface. A heap is a tree with the property that each node is the
// minimum-valued node in its subtree.
//
// The minimum element in the tree is the root, at index 0.
//
// A heap is a common way to implement a priority queue. To build a priority
// queue, implement the Heap interface with the (negative) priority as the
// ordering for the Less method, so Push adds items while Pop removes the
// highest-priority item from the queue. The Examples include such an
// implementation; the file example_pq_test.go has the complete source.
package heapimport "sort"// The Interface type describes the requirements
// for a type using the routines in this package.
// Any type that implements it may be used as a
// min-heap with the following invariants (established after
// [Init] has been called or if the data is empty or sorted):
//
//	!h.Less(j, i) for 0 <= i < h.Len() and 2*i+1 <= j <= 2*i+2 and j < h.Len()
//
// Note that [Push] and [Pop] in this interface are for package heap's
// implementation to call. To add and remove things from the heap,
// use [heap.Push] and [heap.Pop].堆的接口,定义实现该接口内部的全部方法后,我们定义的类就实现了堆
下面是sort.Interface的实现,内部有三个方法,分别是Len()、Less()、Swap()
通过Less()方法,以此确定建立的是大根堆,还是小根堆// An implementation of Interface can be sorted by the routines in this package.
// The methods refer to elements of the underlying collection by integer index.
type Interface interface {// Len is the number of elements in the collection.Len() int// Less reports whether the element with index i// must sort before the element with index j.//// If both Less(i, j) and Less(j, i) are false,// then the elements at index i and j are considered equal.// Sort may place equal elements in any order in the final result,// while Stable preserves the original input order of equal elements.//// Less must describe a transitive ordering://  - if both Less(i, j) and Less(j, k) are true, then Less(i, k) must be true as well.//  - if both Less(i, j) and Less(j, k) are false, then Less(i, k) must be false as well.//// Note that floating-point comparison (the < operator on float32 or float64 values)// is not a transitive ordering when not-a-number (NaN) values are involved.// See Float64Slice.Less for a correct implementation for floating-point values.Less(i, j int) bool// Swap swaps the elements with indexes i and j.Swap(i, j int)
}^
|
|
| 
重点type Interface interface {sort.InterfacePush(x any) // add x as element Len()Pop() any   // remove and return element Len() - 1.
}|
|
|
——init方法,将实现接口的类init进行建堆// Init establishes the heap invariants required by the other routines in this package.
// Init is idempotent with respect to the heap invariants
// and may be called whenever the heap invariants may have been invalidated.
// The complexity is O(n) where n = h.Len().
func Init(h Interface) {// heapifyn := h.Len()for i := n/2 - 1; i >= 0; i-- {down(h, i, n)}
}Push插入元素// Push pushes the element x onto the heap.
// The complexity is O(log n) where n = h.Len().
func Push(h Interface, x any) {h.Push(x)up(h, h.Len()-1)
}Pop弹出元素// Pop removes and returns the minimum element (according to Less) from the heap.
// The complexity is O(log n) where n = h.Len().
// Pop is equivalent to [Remove](h, 0).
func Pop(h Interface) any {n := h.Len() - 1h.Swap(0, n)down(h, 0, n)return h.Pop()
}Remove移除元素// Remove removes and returns the element at index i from the heap.
// The complexity is O(log n) where n = h.Len().
func Remove(h Interface, i int) any {n := h.Len() - 1if n != i {h.Swap(i, n)if !down(h, i, n) {up(h, i)}}return h.Pop()
}Fix重新调整堆// Fix re-establishes the heap ordering after the element at index i has changed its value.
// Changing the value of the element at index i and then calling Fix is equivalent to,
// but less expensive than, calling [Remove](h, i) followed by a Push of the new value.
// The complexity is O(log n) where n = h.Len().
func Fix(h Interface, i int) {if !down(h, i, h.Len()) {up(h, i)}
}堆调整的两个重要方法,up和down,具体我没看,我们可以自己写出更简单更好理解的heapInsert和heapifyfunc up(h Interface, j int) {for {i := (j - 1) / 2 // parentif i == j || !h.Less(j, i) {break}h.Swap(i, j)j = i}
}func down(h Interface, i0, n int) bool {i := i0for {j1 := 2*i + 1if j1 >= n || j1 < 0 { // j1 < 0 after int overflowbreak}j := j1 // left childif j2 := j1 + 1; j2 < n && h.Less(j2, j1) {j = j2 // = 2*i + 2  // right child}if !h.Less(j, i) {break}h.Swap(i, j)i = j}return i > i0
}

2、自己实现堆

自己实现堆,最重要的就是heapInsert和heapify方法,通过这两个方法,以此保证正确实现堆结构。

heapInsert方法:新来的一个元素,新来的元素若大于他的父节点元素,则上升,确定其应该在堆中的正确位置,实现大根堆

for循环虽然只有一个判断,却包含了另一层判断,当index到达0位置后,循环也会停止

func heapInsert(arr []int, index int) {for arr[index] > arr[(index-1)/2] {arr[index], arr[(index-1)/2] = arr[(index-1)/2], arr[index]index = (index - 1) / 2}
}

heapify方法,节点位置为最大值,实现大根堆

func heapify(arr []int, index int, heapsize int) {//左孩子的位置left := index*2 + 1for left < heapsize {//largest的含义为:节点和子节点中的最大值largest := left 	//一开始放在左孩子上//如果存在右孩子,并且右孩子的值比左孩子大,以此选出左右孩子中的较大节点if left+1 < heapsize && arr[left] < arr[left+1] {//在largest放在右孩子的位置largest = left + 1}//判断largest和index节点位置谁大if arr[index] > arr[largest] {//若节点大于largest,则largest来到index位置largest = index}//index位置为最大,则退出循环,不需要向下进行if largest == index {break}//交换节点位置arr[largest], arr[index] = arr[index], arr[largest]//index来到largest位置,继续下次循环index = largest//left继续来到左孩子的位置left = index * 2 + 1}
}

建堆

// 建堆
func buildHeap(arr []int) {//从上至下建堆//for i := 0; i < len(arr); i++ {//	heapInsert(arr, i)//}//从下至上建堆for i := len(arr) - 1; i >= 0; i-- {heapify(arr, i, len(arr))}}

堆排序

能够自己实现堆之后,实现堆排序就十分简单了。

若我们实现了大根堆,那么每次堆顶元素就是最大值,那么只需要堆顶与最后一个元素进行交换,交换后,堆的大小减1,然后在将交换后位于第一位的元素使用heapify让其下降,这样循环进行,最后堆的大小减到0,那么就实现了排序。

func heapSort(arr []int) {heapSize := len(arr)heapSize--arr[0], arr[heapSize] = arr[heapSize], arr[0]for heapSize > 0 {heapify(arr, 0, heapSize)heapSize--arr[0], arr[heapSize] = arr[heapSize], arr[0]}
}

Leetcode堆相关题目

(累了,下次再写)

1、215. 数组中的第K个最大元素 - 力扣(LeetCode)

求数组中第K大的元素,有很简单的方法,将数组进行排序,返回第k个数,即为第k大的数。

题目要求时间复杂度为O(N),可以使用桶排序,这道题目给了数据范围,确实可以用。

题解里还有改进的快速排序,也能达到O(N)的时间复杂度。

但我们主要还是使用堆来完成。

//题解标准答案
func findKthLargest(nums []int, k int) int {//建立大根堆buildHeap(nums)heapSize := len(nums)for i := len(nums) - 1; i >= len(nums)-k+1; i-- {nums[0], nums[i] = nums[i], nums[0]heapSize--heapify(nums, 0, heapSize)}return nums[0]
}//我觉得不好理解,就自己写了i从0——K,但是需要加判断,要不会出现nums[-1]的报错
func findKthLargest(nums []int, K int) int {buildHeap(nums)heapSize := len(nums)nums[0], nums[heapSize-1] = nums[heapSize-1], nums[0]for i:= 0; i < K; i++ {heapSize--heapify(nums,0,heapSize)if heapSize-1 >= 0{nums[0], nums[heapSize-1] = nums[heapSize-1], nums[0]} else {return nums[heapSize]}}return nums[heapSize]
}func heapify(arr []int, index int, heapsize int) {//左孩子的位置left := index*2 + 1for left < heapsize {//largest的含义为:节点和子节点中的最大值largest := left //一开始放在左孩子上//如果存在右孩子,并且右孩子的值比左孩子大,以此选出左右孩子中的较大节点if left+1 < heapsize && arr[left] < arr[left+1] {//在largest放在右孩子的位置largest = left + 1}//判断largest和index节点位置谁大if arr[index] > arr[largest] {//若节点大于largest,则largest来到index位置largest = index}//index位置为最大,则退出循环,不需要向下进行if largest == index {break}//交换节点位置arr[largest], arr[index] = arr[index], arr[largest]//index来到largest位置,继续下次循环index = largest//left继续来到左孩子的位置left = index*2 + 1}
}func buildHeap(arr []int) {//从上至下建堆//for i := 0; i < len(arr); i++ {//	heapInsert(arr, i)//}//从下至上建堆for i := len(arr) - 1; i >= 0; i-- {heapify(arr, i, len(arr))}}

2、502. IPO - 力扣(LeetCode)

3、373. 查找和最小的 K 对数字 - 力扣(LeetCode)

TODO

4、295. 数据流的中位数 - 力扣(LeetCode)

TODO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/64748.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java开发入门学习五-流程控制

流程控制语句 if&#xff0c; if...else&#xff0c; if..else if..else 与前端相同 略 switch case 与前端不同的是case不能使用表达式&#xff0c;使用表达式会报错 class TestSwitch {public static void main(String[] args) {// switch 表达式只能是特定的数据类型…

豆包MarsCode测评:编程效率再提升

豆包MarsCode测评&#xff1a;编程效率再提升 本文正在参与豆包MarsCode AI 编程体验家活动 随着人工智能技术的发展&#xff0c;编程的方式也在悄然发生变化。最近&#xff0c;豆包推出的 AI 编程工具 MarsCode 在开发者社区引发了不小的关注。这是一款支持多种主流编程语言…

FFmpeg 框架简介和文件解复用

文章目录 ffmpeg框架简介libavformat库libavcodec库libavdevice库 复用&#xff08;muxers&#xff09;和解复用&#xff08;demuxers&#xff09;容器格式FLVScript Tag Data结构&#xff08;脚本类型、帧类型&#xff09;Audio Tag Data结构&#xff08;音频Tag&#xff09;V…

Unity开发哪里下载安卓Android-NDK-r21d,外加Android Studio打包实验

NDK下载方法&#xff08;是r21d,不是r21e, 不是abc, 是d版本呢) google的东西&#xff0c;居然是完全开源的 真的不是很多公司能做到&#xff0c;和那种伪搜索引擎是不同的 到底什么时候google才会开始造车 不过风险很多&#xff0c;最好不要合资&#xff0c;风险更大 Andr…

leetcode-128.最长连续序列-day14

为什么我感觉上述代码时间复杂度接近O(2n), 虽然有while循环&#xff0c;但是前面有个if判断&#xff0c;能进入while循环的也不多&#xff0c;while循环就相当于两个for循环&#xff0c;但不是嵌套类型的&#xff1a; 变量作用域问题&#xff1a;

人工智能入门是先看西瓜书还是先看花书?

在人工智能入门时&#xff0c;关于先看《机器学习》&#xff08;西瓜书&#xff09;还是先看《深度学习》&#xff08;花书&#xff09;的问题&#xff0c;实际上取决于个人的学习目标和背景。 《机器学习》&#xff08;西瓜书&#xff09;由周志华教授撰写&#xff0c;是一本…

B 站数据库负责人赵月顺:助力海内外业务增长,百套 TiDB 的选型与运维实战

导读 B 站对 TiDB 的应用已相当广泛&#xff0c;被应用在了 包括视频观看、一键三连、发送弹幕、撰写评论、阅读漫画以及视频后端的存储等场景&#xff0c; 目前拥有近 100 套集群。 本文由 B 站数据库负责人赵月顺撰写&#xff0c; 详细介绍了 B 站面临业务增长选择 TiDB 的…

二九(vue2-05)、父子通信v-model、sync、ref、¥nextTick、自定义指令、具名插槽、作用域插槽、综合案例 - 商品列表

1. 进阶语法 1.1 v-model 简化代码 App.vue <template><!-- 11-src-下拉封装 --><div class"app"><!-- <BaseSelect :cityId"selectId" changeId"handleChangeId"></BaseSelect> --><!-- v-model 简化…

flask-admin+Flask-WTF 实现实现增删改查

背景&#xff1a; flask-adminflask-wtf在网上可以搜索到很多资料&#xff0c;但有价值的很少&#xff0c;或许是太简单&#xff0c;或者是很少人这么用&#xff0c;或者。。。&#xff0c;本文将作者近礼拜摸索到的一点经验分享出来&#xff0c;给自己做个记录。 材料&#…

Linux下基于最新稳定版ESP-IDF5.3.2开发esp32s3入门任务间的通讯-消息队列【入门四】

继续上一篇任务创建 【Linux下基于最新稳定版ESP-IDF5.3.2开发esp32s3入门任务间的通讯-信号量【入门三】-CSDN博客】 今天要实现消息队列进行任务的通讯 一、从上一篇信号量通讯demo拷贝一份重命名&#xff0c;还是之前的两个任务&#xff0c;重命名了。 xTaskCreatePinned…

workman服务端开发模式-应用开发-后端api推送修改二

需要修改两个地方&#xff0c;第一个是总控制里面的续token延时&#xff0c;第二个是操作日志记录 一、总控续token延时方法 在根目录下app文件夹下controller文件夹下Base.php中修改isLoginAuth方法&#xff0c;具体代码如下&#xff1a; <?php /*** 总控制* User: 龙哥…

ReactPress 1.6.0:重塑博客体验,引领内容创新

ReactPress 是一个基于Next.js的博客&CMS系统&#xff0c; Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎Star。 体验地址&#xff1a;http://blog.gaoredu.com/ 今天&#xff0c;我们自豪地宣布ReactPress 1.6.0版本的正式发布&#xff0c;…

重拾设计模式--外观模式

文章目录 外观模式&#xff08;Facade Pattern&#xff09;概述定义 外观模式UML图作用 外观模式的结构C 代码示例1C代码示例2总结 外观模式&#xff08;Facade Pattern&#xff09;概述 定义 外观模式是一种结构型设计模式&#xff0c;它为子系统中的一组接口提供了一个统一…

接口测试Day03-postman断言关联

postman常用断言 注意&#xff1a;不需要手敲&#xff0c;点击自动生成 断言响应状态码 Status code&#xff1a;Code is 200 //断言响应状态码为 200 pm.test("Status code is 200", function () {pm.response.to.have.status(200); });pm: postman的实例 test() …

提升专业素养的实用指南

在当今竞争激烈的职场&#xff0c;仅仅拥有专业技能已经不足以立于不败之地。持续提升自身专业素养&#xff0c;才是保持竞争力、实现职业目标的关键。那么&#xff0c;如何才能有效地提升专业素养&#xff0c;在职业道路上走得更稳、更远呢&#xff1f;以下是一些实用性建议&a…

网上球鞋竞拍系统|Java|SSM|VUE| 前后端分离

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html 5⃣️数据库可…

tryhackme-Pre Security-Windows Fundamentals 3(Windows基础知识3)

任务1&#xff1a;Introduction&#xff08;介绍&#xff09; 我们将继续探索 Windows 操作系统。 总结前两个房间&#xff1a; 在 Windows Fundamentals 1 中&#xff0c;我们介绍了桌面、文件系统、用户帐户控制、控制面板、设置和任务管理器。在 Windows Fundamentals 2 中…

pdf转换文本:基于python的tesseract

电脑系统&#xff1a;win10专业版 不能访问需要魔法上网 安装tesseract 在GitHub上下载:tesseract下载地址 找到自己电脑版本下载 双击安装&#xff0c;一路next&#xff0c;除了这一步 第三个加号点开&#xff0c;把带Chinese的都勾选 安装完成后配置环境&#xff0c;Win …

国产云厂商数据库产品--思维导图

为了对比国产云厂商数据库产品&#xff0c;我查阅了各云厂商的官方介绍&#xff0c;墨天轮等平台的部分数据和文章&#xff0c;整理出了简易的思维导图。 会去整理&#xff0c;也是因为有点懵&#xff0c;比如说阿里的PolarDB数据库&#xff0c;看起来就是一个数据库&#xff…

MongoDB(下)

MongoDB 索引 MongoDB 索引有什么用? 和关系型数据库类似&#xff0c;MongoDB 中也有索引。索引的目的主要是用来提高查询效率&#xff0c;如果没有索引的话&#xff0c;MongoDB 必须执行 集合扫描 &#xff0c;即扫描集合中的每个文档&#xff0c;以选择与查询语句匹配的文…