#### golang中【堆】的使用及底层 ####

 声明,本文部分内容摘自:

 Go: 深入理解堆实现及应用-腾讯云开发者社区-腾讯云

数组实现堆 | WXue

堆(Heap)是实现优先队列的数据结构,Go提供了接口和方法来操作堆。

应用

package mainimport ("container/heap""sort"
)/*
题目:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字,滑动窗口每次只向右移动一位,返回滑动窗口中的最大值。示例:输入:nums = [1,3,-1,-3,5,3,6,7], k = 3输出:[3,3,5,5,6,7]解释:滑动窗口的位置                最大值---------------------------------[1 3  -1] -3  5  3  6  7       31 [3  -1  -3] 5  3  6  7       31  3 [-1  -3  5] 3  6  7       51  3  -1 [-3  5  3] 6  7       51  3  -1  -3 [5  3  6] 7       61  3  -1  -3  5 [3  6  7]      7
题解:大根堆可以帮助我们实时维护一系列元素中的最大值。初始时,我们将数组 nums 的前 k 个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组 (num,index),表示元素 num 在数组中的下标为 index。
*/var a []int// heap 实现了标准库的heap.Interface接口
type hp struct {sort.IntSlice // type IntSlice []int
}func (h hp) Less(i, j int) bool {return a[h.IntSlice[i]] > a[h.IntSlice[j]]
}
func (h *hp) Push(v interface{}) {h.IntSlice = append(h.IntSlice, v.(int))
}
func (h *hp) Pop() interface{} {a := h.IntSlicev := a[len(a)-1]h.IntSlice = a[:len(a)-1]return v
}func maxSlidingWindow(nums []int, k int) (ans []int) {ans = make([]int, 1, len(nums)-k+1)a = nums// 初始化堆(优先队列)queue := &hp{make([]int, k)} // 优先队列for i := 0; i < k; i++ {queue.IntSlice[i] = i // 注意堆里存的是数组下标而非数组值,对应Less函数里的比较时需要a[h.IntSlice[i]]来比较值}heap.Init(queue) // 初始化+向下调整// 赋值ans[0],因为不需要判断IntSlice[0]的元素是不是在边界外的左侧ans[0] = nums[queue.IntSlice[0]] // IntSlice[0] 下标为0=数组IntSlice的头部=堆顶元素// 窗口滑动for i := k; i < len(nums); i++ {heap.Push(queue, i)            // 入堆+向上调整for queue.IntSlice[0] <= i-k { // 判断IntSlice[0]的元素是不是在边界外的左侧heap.Pop(queue) // 出堆+向下调整}ans = append(ans, nums[queue.IntSlice[0]]) // IntSlice[0] 下标为0=数组头部=堆顶元素}return ans
}func main() {res := maxSlidingWindow([]int{1, 3, -1, -3, 5, 3, 6, 7}, 3)println(res)
}

底层

包:container/heap

接口:heap.Interface

源码:

type Interface interface {sort.InterfacePush(x interface{}) // 添加元素Pop() interface{}   // 弹出元素
}

其中,注意,实现heap.Interface接口需要嵌入sort.Interface,后者包含Len()、Less(i, j int) bool和Swap(i, j int)方法,用于确定元素间的排序。

全部源码:

type Interface interface {sort.InterfacePush(x any) // add x as element Len()Pop() any   // remove and return element Len() - 1.
}func Init(h Interface) {// heapifyn := h.Len()for i := n/2 - 1; i >= 0; i-- {down(h, i, n)}
}// 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)
}func Pop(h Interface) any {n := h.Len() - 1h.Swap(0, n)down(h, 0, n)return h.Pop()
}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()
}func Fix(h Interface, i int) {if !down(h, i, h.Len()) {up(h, i)}
}func 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
}

其中:

    ① 初始化(Init): 对一个未排序的切片构建堆。这是通过down方法实现的,down方法确保元素下沉到正确的位置,维持堆的性质。

    ② 添加元素(Push): 元素被添加到切片的末尾,然后通过up方法上浮到正确的位置。

注意:标准库中的push函数中,第一行调用的【h.Push(x)】是上层业务代码中自行实现的heap.Interface的堆实例的push方法。

func Push(h Interface, x any) {
    h.Push(x)
    up(h, h.Len()-1)
}

    ③ 删除元素(Pop): 堆顶元素(切片的第一个元素)被移动到切片末尾并返回,然后新的堆顶元素通过down方法恢复堆的性质。

    ④ 删除任意元素(Remove): 类似Pop,但可以移除指定位置的元素。此操作需要综合up和down方法来调整堆。

    ⑤ 修改元素并调整堆(Fix): 如果堆中某个元素被外部修改了(比如优先级改变),Fix方法会根据这个修改后的新值重新调整堆。

堆是一颗完全二叉树,可由数组表示

完全二叉树,逐层而下,从左到右,结点的位置完全由其序号觉得,因此可以用数组来实现。

计算各结点下标的公式,其中 𝑟𝑟 表示结点的下标,范围在 0 ~ n-1 之间,n 是二叉树结点的总数。

𝑃𝑎𝑟𝑒𝑛𝑡(𝑟)=⌊(𝑟−1)/2⌋𝑃𝑎𝑟𝑒𝑛𝑡(𝑟)=⌊(𝑟−1)/2⌋ 向下取整,当 𝑟≠0𝑟≠0 时

𝐿𝑒𝑓𝑡𝑐ℎ𝑖𝑙𝑑(𝑟)=2𝑟+1𝐿𝑒𝑓𝑡𝑐ℎ𝑖𝑙𝑑(𝑟)=2𝑟+1, 当 2𝑟+1<𝑛2𝑟+1<𝑛 时

𝑅𝑖𝑔ℎ𝑡𝑐ℎ𝑖𝑙𝑑(𝑟)=2𝑟+2𝑅𝑖𝑔ℎ𝑡𝑐ℎ𝑖𝑙𝑑(𝑟)=2𝑟+2, 当 2𝑟+2<𝑛2𝑟+2<𝑛 时

𝐿𝑒𝑓𝑡𝑠𝑖𝑏𝑙𝑖𝑛𝑔()=𝑟−1𝐿𝑒𝑓𝑡𝑠𝑖𝑏𝑙𝑖𝑛𝑔()=𝑟−1, 当 r 为偶数时

𝑅𝑖𝑔ℎ𝑡𝑠𝑖𝑏𝑙𝑖𝑛𝑔()=𝑟+1𝑅𝑖𝑔ℎ𝑡𝑠𝑖𝑏𝑙𝑖𝑛𝑔()=𝑟+1 , 当 r 为奇数并且 𝑟+1<𝑛𝑟+1<𝑛 时

20200328Build_heap

插入数值:在堆的末尾插入,然后不断向上提升,直到没有大小颠倒。

删除数值:首先把堆的最后一个节点的数值放到根上去,并且删除最后一个节点,然后不断向下交换直到没有大小颠倒为止。向下交换的时候如果 2 个儿子都比自己小,那么选择数值较小的儿子进行交换。

复杂度:建堆需要 On 的时间,但删除、插入都和树深度成正比,时间复杂度是 O𝑛𝑙𝑜𝑔𝑛。

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

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

相关文章

结构方程模型-验证性因子分析模型

初级 第7讲 验证性因子分析模_哔哩哔哩_bilibili

claude3.5写作——《基于灰色预测的中国人口数量预测》

文章目录 站点和提问引言一、灰色预测模型介绍二、中国历年人口数据三、灰色预测模型的建立1.建立原始序列2.生成1-AGO序列3.计算背景值4.构造数据矩阵并计算参数5.模型检验6.模型预测 四、预测结果分析五、政策建议结语参考文献 站点和提问 站点&#xff1a;中国官方克劳德3.…

光伏电站数据采集方案(基于工业路由器部署)

​ 一、方案概述 本方案采用星创易联SR500工业路由器作为核心网关设备&#xff0c;实现对光伏电站现场数据的实时采集、安全传输和远程监控。SR500具备多接口、多功能、高可靠性等特点&#xff0c;能够满足光伏电站数据采集的各种需求。&#xff08;key-iot.com/iotlist/sr500…

RK3568平台(opencv篇)ubuntu18.04上安装opencv环境

一.什么是 OpenCV-Python OpenCV-Python 是一个 Python 绑定库&#xff0c;旨在解决计算机视觉问题。   Python 是一种由 Guido van Rossum 开发的通用编程语言&#xff0c;它很快就变得非常流行&#xff0c;主要是 因为它的简单性和代码可读性。它使程序员能够用更少的代码行…

网络IO模型之多路复用器.md

多路复用是什么&#xff1f;怎么理解&#xff1f; 本文主要涉及为 程序中处理网络IO时的模型&#xff0c;对于系统内核而言网络IO模型。这里只做普及使用 前置知识&#xff0c;什么是IO&#xff1f;怎么理解IO IO其实就是In和Out。中文翻译是输入和输出&#xff0c;只要涉及到输…

clone()方法

在Java中&#xff0c;clone() 方法是一个非常有趣且强大的工具&#xff0c;用于创建对象的一个副本。这个方法位于 Object 类中&#xff0c;因此可以被所有类使用。让我们讨论一下它的几个要点&#xff1a; 什么是克隆&#xff1f; 克隆就是创建一个对象的新副本&#xff0c;这…

2005-2022全国及各省家庭承包耕地流转总面积及经营耕地面积数据(无缺失)

2005-2022全国及各省家庭承包耕地流转总面积及经营耕地面积数据&#xff08;无缺失&#xff09; 1、时间&#xff1a;2005-2022年 2、范围&#xff1a;全国及30省 3、指标&#xff1a;家庭承包耕地流转总面积、家庭承包经营耕地面积、土地流转率、 4、来源&#xff1a;农村…

《web应用技术》第十一次课后作业

验证过滤器进行权限验证的原理。 创建Filter&#xff1a; package com.example.filter;import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException;WebFilter(urlPatterns "/*") public class DemoFilter implements Filter …

adobe pdf设置默认打开是滚动而不是单页视图

上班公司用adobe pdf&#xff0c;自己还不能安装其它软件。 每次打开pdf&#xff0c;总是默认单页视图&#xff0c;修改滚动后&#xff0c;下次打开又 一样&#xff0c;有时候比较烦。 后面打开编辑->首选项&#xff0c; 如下修改&#xff0c;下次打开就是默认滚动了

Websocket通信实战项目(图片互传应用)+PyQt界面+python异步编程(async) (上)服务器端python实现

Rqtz : 个人主页 ​​ 共享IT之美&#xff0c;共创机器未来 ​ Sharing the Beauty of IT and Creating the Future of Machines Together 目录 项目背景 ​编辑​专有名词介绍 服务器GUI展示 功能(位置见上图序号) 客户端GUI展示&#xff08;h5cssjs&#xf…

flask的进阶使用方法

【 一 】一对多关系 # 1 一对一 [本质就是一对多--》多的那个唯一] # 2 一对多 # 3 多对多1.1 关系 #### 一对多关系 class Hobby(Base):__tablename__ hobbyid Column(Integer, primary_keyTrue)caption Column(String(50), default篮球)def __str__(self):return sel…

pydub、ffmpeg 音频文件声道选择转换、采样率更改

快速查看音频通道数和每个通道能力判断具体哪个通道说话&#xff1b;一般能量大的那个算是说话 import wave from pydub import AudioSegment import numpy as npdef read_wav_file(file_path):with wave.open(file_path, rb) as wav_file:params wav_file.getparams()num_cha…

Android10以上实现获取设备序列号功能

Android10以上实现获取设备唯一标识&#xff0c;目前只支持华为和荣耀设备。实现原理&#xff1a;通过无障碍服务读取序列号界面。 public class DeviceHelper implements Application.ActivityLifecycleCallbacks {static final String TAG "WADQ_DeviceHelper";s…

Zoom使用的基本步骤和注意事项

Zoom是一款功能强大的视频会议软件&#xff0c;广泛应用于远程办公、在线教育、团队协作等多个场景。以下是Zoom使用的基本步骤和注意事项&#xff1a; 一、注册与登录 注册Zoom账户&#xff1a; 访问Zoom官方网站&#xff08;如zoom.us&#xff09;&#xff0c;点击“注册”…

mybatis实现动态sql

第一章、动态SQL MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验&#xff0c;你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格&#xff0c;还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特…

[ C++ ] 深入理解模板( 进 阶 )

目录 非类型模板参数 类模板没有实例化的情况 模板的特化 注意函数特化中遇到的问题 建议&#xff1a;&#xff08;直接使用函数重载&#xff09; 类模板特化 全特化 偏特化 偏特化有以下两种表现方式&#xff1a; 部分特化&#xff08;将模板参数类表中的一部分参数特化…

旅游系统(附管理端+前台)PHP源码

一. 前言 今天小编给大家带来了一款可学习&#xff0c;可商用的&#xff0c;旅游系统 源码&#xff0c;支持二开&#xff0c;无加密。支持景点管理&#xff0c;登录&#xff0c;景点预定&#xff0c;意见反馈&#xff0c;统计等功能。详细界面和功能见下面视频演示。 二. 视频…

【flutter问题记录】 无效的源发行版:17

问题描述 在看开源项目的时候&#xff0c;clone下来后一直编译失败&#xff0c;提示&#xff1a;无效的源发行版:17&#xff0c;看描述大概是jdk的版本问题&#xff0c;但是在Android studio各种指定都无用&#xff0c;网上资料也没有flutter项目的解决方案&#xff0c;最后在…

广州自闭症机构哪家好?

在广州&#xff0c;众多的自闭症康复机构中&#xff0c;星贝育园自闭症儿童康复学校以其独特的优势脱颖而出。 一、专业的师资团队 我们拥有一支经验丰富、专业素养极高的师资队伍。每位老师都经过严格的专业培训&#xff0c;深入了解自闭症儿童的特点和需求。他们不仅具…