Golang heap源码简单走读

2023-11-03

golang heap小根堆源码走读

heap概览

在golang中,通过heap给出了一个实现小根堆的接口。

type Interface interface {
	sort.Interface
	Push(x interface{}) 
	Pop() interface{}  
}

由于小根堆中,需要根据容器中的元素大小来进行比较以确定元素在堆中的位置。因此也需要实现sort的接口。

type Interface interface {
	Len() int
	Less(i, j int) bool
	Swap(i, j int)
}

因此,如果需要实现一个heap,需要实现Push(),Pop()(从堆顶弹出元素),Len(),Less()(进行元素之间比较的方法)以及Swap()方法。

heap已经实现的方法

heap的初始化

func Init(h Interface) {
	n := h.Len()
	for i := n/2 - 1; i >= 0; i-- {
		down(h, i, n)
	}
}

在heap的初始化方法中,值得注意的是,不同于java方法,在调用这个方法的时候,堆中的元素容器应该已经完成了数据的存放,这里的初始化不是初始化存储空间返回一个空的容器,而是对一个已经充满元素的容器进行堆排序。首先会获取堆的大小n,取决于堆的性质,将会从堆的n/2-1位置开始通过down()方法初始化,这个位置是堆中倒数第二层最后一个拥有子节点的节点,之后将会不断从当前层不断向左,遍历完当前层之后再从上一层的最右边开始。

func down(h Interface, i0, n int) bool {
	i := i0 // i是方法中当前节点的下标
	for {
		j1 := 2*i + 1 // 获取当前节点的左子节点
		if j1 >= n || j1 < 0 { 
			break // 如果当前节点的左子节点不存在则结束这一轮
		}
		j := j1 // j为当前节点的左子节点
		if j2 := j1 + 1; j2 < n && h.Less(j2, j1) { // 此处Less()方法为上文提到需要扩展实现的元素比较方法
			j = j2 // 将节点的左节点和右节点进行比较,j赋值为其中较小的下标
		}
		if !h.Less(j, i) {
			break 
		}
		h.Swap(i, j) // 如果当前节点小于其子节点中的较小值,通过扩展的Swap()方法交换,将较小的值替换到父节点上,并且将会从当前节点继续往下与其子节点比较,直到达到最底层或者其两个子节点逗都比自己大
		i = j
	}
	return i > i0
}

顾名思义,down()实则是将一个节点不断从堆中不断下移直到达到其对应位置的一个过程。由于n/2-1保证了遍历的开始位置是堆中最后一个拥有子节点的位置,因此可以保证最后一层每一个子节点都能参与到一轮小根堆的初始化。一轮down()下来,将会保证容器中的最小值将会处于堆顶,只需要通过Pop()方法将栈顶的元素弹出就可以得到堆中最小的值。这可以适用于优先级队列等场景的实现。

heap的添加

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

当通过Push()方法往堆中添加元素的时候,可以先简单的将元素放到最后一层的叶子节点上,之后通过up()方法从最后一个子节点开始,将该新元素从堆底up到堆中的一个合适的位置上。

func up(h Interface, j int) {
	for {
		i := (j - 1) / 2 // 当前节点的父节点
		if i == j || !h.Less(j, i) {
			break
		}
		h.Swap(i, j) // 如果当前节点小于其父节点,将该节点与其父节点交换,并继续从新的位置向其父节点进行比较,直到下标为0达到堆顶或者遇到比其更小的父节点
		j = i
	}
}

对应down()的取名,up()是将一个节点从当前位置不断往堆的更上层前进的过程。这里的Push()操作的时间复杂度为O(logn)。

heap的弹出

func Pop(h Interface) interface{} {
	n := h.Len() - 1
	h.Swap(0, n)
	down(h, 0, n)
	return h.Pop()
}

Heap的弹出,只需要将堆顶的元素与堆的末尾元素调换,将其放到堆的末尾默认移除,不参与后续堆的调整即可。之后将当前所处的堆顶元素通过down()方法不断下移到其应该处在的位置即可。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Golang heap源码简单走读 的相关文章

随机推荐

  • 行为型设计模式之策略模式【设计模式系列】

    系列文章目录 C 技能系列 Linux通信架构系列 C 高性能优化编程系列 深入理解软件架构设计系列 高级C 并发线程编程 设计模式系列 期待你的关注哦 现在的一切都是为将来的梦想编织翅膀 让梦想在现实中展翅高飞 Now everythin
  • C++基础---递归函数

    1 递归函数 1 1 递归函数的定义 递归函数 即在函数体中出现调用自身的函数 即函数Func Type a 直接或间接调用函数本身 递归函数 在数学上 关于递归函数的定义如下 对于某一函数f x 其定义域是集合A 那么若对于A集合中的某一
  • centos安装常见软件

    安装tar yum install y tar 安装zip yum install unzip y 安装上传 yum y install lrzsz y 安装git 方式一 yum install git y 方式二 开发会用的软件 yum
  • STM32F1应用DMA——串口收发不定长数据

    STM32F1应用DMA 串口收发不定长数据 使用STM32自带DMA传输数据 可以减轻CPU负担 只需设置一些参数即可发送想要发送的数据 以下是STM32F1系列芯片测试过的部分代码 可实现DMA串口收发数据 下图来自STM32官网的手册
  • webrtc中peerconnection_client生成vs工程文件

    下面是将peerconnection client从整个webrtc工程文件中分离出来的过程记录 一 webrtc项目的本地编译 生成Ninja配置文件 gn gen target x64 args is clang false use l
  • Matplotlib绘制动图以及绘制平滑曲线

    文章目录 绘制动图 FuncAnimation 方法 ArtistAnimation 方法 绘制平滑曲线 使用 scipy ndimage gaussian filter1d 高斯核类绘制平滑曲线 使用 scipy interpolate
  • python怎么做多个矩阵_用Python程序添加两个矩阵

    用Python程序添加两个矩阵 在此程序中 您将学习使用嵌套循环和Next列表理解来添加两个矩阵 并显示它们 要理解此示例 您应该了解以下Python编程主题 在Python中 我们可以将矩阵实现为嵌套列表 列表内的列表 我们可以将每个元素
  • openmpi编译安装

    概念原理 OpenMPI是一个免费的 开源的MPI实现 兼容MPI 1和MPI 2标准 OpenMPI由开源社区开发维护 支持大多数类型的HPC平台 并具有很高的性能 功能描述 OpenMPI借助TCP IP网络连接的多台计算机 以此分发数
  • 经典多模态模型

    整点传统多模态学习 下游任务 在讲模型之前 我们先说说 传统多模态任务是下游任务 图文检索 Image Text Retrieval 里面包含图像到文本检索 文本到图像检索 给定一个数据库 搜索到ground truth的图像文本对 因为是
  • NDIS网络数据监控程序NDISMonitor(2)-----驱动与应用的中间层NdisHook

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家拍砖 本工程是驱动vpcknt的一个封闭层而已 比较简单 一 导出的API接口分析 1 Start 1 加载驱动vpcknt sys vpckn
  • List转换String,String转List的几种方法

    一 List转String的方法 将一个Java集合List转换为String方法比较多 可以使用String join StringBuilder Stream流等方法 下面举几个常用的示例 1 使用String join 方法 impo
  • c51语言的指针分几类,- 第五课 C51变量

    sfr P1 0x90 这里没有使用预定义文件 sbit P1 0 P1 0 而是自己定义特殊寄存器 sbit P1 7 0x90 7 之前我们使用的预定义文件其实就是这个作用 sbit P1 1 0x91 这里分别定义P1端口和P10 P
  • react-router-dom v6的变化

    react router dom v6 原文地址 1 useNavigate替代useHistory 在v6版本useHistory被新hookuseNavigate代替 用法也发生的很大的变化 v5 import useHistory f
  • 如何画出频谱图 matlab

    如何画出频谱图 matlab matlab 代码 绘制出的图片 matlab 代码 fs 100 sample frequency Hz t 0 1 fs 10 1 fs 10 second span time vector x 1 3 s
  • R中prophet包说明文档(一)

    名称 自动预测过程 版本 0 2 1 日期 2017 11 08 描述 实现了一个时间序列的预测过程 基于能够拟合年度 周等周期以及假期等因素的非线性趋势的加法模型 模型要求至少一年以上的周期性历史数据 prophet模型对于缺失值 趋势突
  • PHP实现网站访问量计数器 两种方法

    1 原生 简单的网站访问量计数器实现 具体如下 首先说明思路 1 用户向服务器发出访问请求 2 服务器读取访问次数文件 1 向客户端返回 3 服务器保存新的浏览次数 4 新用户访问 重复123即可 解决方案 主要算法 1 数据文件 coun
  • 使用Clion 阅读/修改/注释 Linux 内核源码

    前言 其实 bootlin就是一个听不错的阅读源码的工具了 可以非常方便的帮我们查阅函数 宏的定义 引用等等 而且是基于浏览器 对我们本机的配置没有什么过高的要求 但是如果想要做一些注释 修改 那我们就要将源码下载到本地了 这个时候我们可能
  • 数据仓库灵魂30问之传统数仓和大数据数仓的异同?有哪些大的变化?

    不同点 特性 传统数仓 大数据数仓 数据存储位置 关系型数据库 HDFS 数据集市位置 MPP平台 HDFS 数据多样性 结构化数据 结构化数据 非结构化数据 半结构化数据 节点数量 几千 几千 几万 数据量 TB级别 PB级别 商业价值
  • 【转载】【NLP】使用 PyTorch 通过 Hugging Face 使用 BERT 和 Transformers 进行情感分析

    参考 https blog csdn net sikh 0529 article details 127950840 目的 用transformers加载自己的数据进行训练 然后做预测 知识点补充 什么是BERT BERT 在本文中介绍 代
  • Golang heap源码简单走读

    golang heap小根堆源码走读 heap概览 在golang中 通过heap给出了一个实现小根堆的接口 type Interface interface sort Interface Push x interface Pop inte