Python - 深夜数据结构与算法之 Heap Binary Heap

目录

一.引言

二.堆与二叉堆介绍

1.Heap 堆

2.Binary Heap 二叉堆

3.HeapifyUp 添加节点

4.HeapifyDown 删除节点

5.Heap 时间复杂度

6.Insert & Delete 代码实现

三.经典算法实战

1.Smallest-K [M14]

2.Sliding-Window-Max [239]

3.Ugly-Number [264]

4.Top-K-Freq-Ele [347]

四.总结


一.引言

前面介绍了树和二叉树的概念,接下来介绍 Heap 堆和 Binary Heap 二叉堆,这里堆是一个抽象的数据结构,二叉堆只是其实现的一种形式,并不代表所有堆都使用二叉树实现。

二.堆与二叉堆介绍

1.Heap 堆

堆主要分为两种表现形式,最大堆 or 最小堆,也有叫大根堆 or 小根堆的,其可以在常数时间 o(1) 获取到最大 or 最小的元素,其一般包含 3 个基础 API:

◆ find-max - 寻找堆中的最大值

◆ delete-max - 删除堆中的最大值

◆ insert - 向堆中插入一个新元素

注意 📢 delete 和 insert 操作都会导致堆的结构破坏,所以需要重新调整父子节点位置从而维护堆的原始性质。

2.Binary Heap 二叉堆

二叉堆是基于二叉树实现的 Heap,不过有一点需要注意这里的二叉树是完全二叉树,假设其深度为 h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。也就是除了最后一层叶子 🍃 节点外,其余层的节点都是满的且没有 None。 同时二叉堆一般基于 List 实现,以下述列表为例:

[110, 100, 90, 40, 80, 20, 60, 10, 30, 50, 70]

完全二叉树可以看作是满二叉树从尾部叶子节点开始清退,即数组尾部清退,剩下的都是满足完全二叉树的,这样做主要是为了提高效率,如果过多节点为 None,二叉树的深度 h 增加,那么搜索的时间复杂度也会逐渐增加且浪费空间。 基于完全二叉树形态,其具备以下性质:

通过索引 i 我们可以获取对应索引的左右孩子节点和父节点的位置 [如果有的话],假设是 k 叉树的话,索引 i 的 k 个孩子索引为 k*i + 1、k*i + 2、k*i+3 以此类推,父节点索引则为 floor((i-1)/k)。 

3.HeapifyUp 添加节点

基于二叉堆 index 及其父子节点的关系,insert 操作首先将节点插入数组尾部,然后依次与父节点比较向上移动直到无法移动,增加元素结束。

◆ 插入 85 到 Heap

◆ 元素添加到队尾 

◆ 持续向上移动并交换

4.HeapifyDown 删除节点

删除元素首先将 root 节点移出,随后将尾节点替换至 root,与左右节点中较大的节点替换位置,如此循环下去,直到不能移动并恢复堆的性质。 

5.Heap 时间复杂度

再次重申下堆是一个抽象的结构,其有多种实现方式,就像是接口一样。这里最常见的实现方法为二叉堆,而随着实现方法的复杂,其 insert 和 delete 的复杂度也会更加友好。这里 Binaey Heap 的 find Min/Max 的时间复杂度为 o(1),其余时间复杂度为 o(logn)。

6.Insert & Delete 代码实现

◆ HeapifyUp

这里用了指针的方式,不断将较小的 Parent 节点与 Insert 节点互换,最终停止循环。

◆ HeapifyDown

增加元素只需要与 Parent 节点比较,但是删除节点需要和左右子节点比较,所以需要使用 maxChild 函数寻找更大的节点进行交换,这样才能满足堆得性质。 

三.经典算法实战

1.Smallest-K [M14]

最小的 K 个数: https://leetcode.cn/problems/smallest-k-lcci/

题目分析

既然是在堆的讲解下,所以我们可以直接使用 Heap,由于是最小的 k 个数,所以可以使用最小堆,每次返回堆顶元素并更新堆,执行 k 次即可。

小根堆

class Solution(object):def smallestK(self, arr, k):""":type arr: List[int]:type k: int:rtype: List[int]"""heapq.heapify(arr)res = []for i in range(k):res.append(heapq.heappop(arr))return res

python 的 heapq 默认为最小堆,直接将元素通过 o(n) 复杂度构建一个 Heap,随后 pop k 次堆顶的元素即可。当然 heapq 提供 nsmallest 和 nlarggest (k, nums) 的函数可以直接获取前 k 个最小、最大的元素,其等价于 nums.sort()[:k]。

2.Sliding-Window-Max [239]

最大滑动窗口: https://leetcode.cn/problems/sliding-window-maximum/description/

题目分析

之前 ArrayList 部门我们通过 dequeue 双端队列实现了 k-window-max 的算法,一个技巧就是我们需要记录每个元素的索引,从而判断是否在窗口中。这里 python 默认的 heapq 为小根堆,通过 -1 * nums 转换为大根堆,随后遍历 k 窗口,取出堆顶最大值即可,注意在堆的维护过程中,排除索引 index 在 k-window 之外的元素。

大根堆

class Solution(object):def maxSlidingWindow(self, nums, k):n = len(nums)# 构造最大堆q = [(-nums[i], i) for i in range(k)]heapq.heapify(q)# 先获取前k个元素的 maxans = [-q[0][0]]for i in range(k, n):# 向堆添加新的元素heapq.heappush(q, (-nums[i], i))# 堆顶为窗口外元素则去除该元素while q[0][1] <= i - k:heapq.heappop(q)# pop 后仍然保持堆的结构,弹出堆顶最大值ans.append(-q[0][0])return ans

对于最大、最小 k 个数的题目,我们都可以想到通过 heap 来实现,工程环境下直接使用对应的库即可。

3.Ugly-Number [264]

丑数: https://leetcode.cn/problems/ugly-number-ii/description/

题目分析 

这个题目主要是理解比较困难,感觉是记忆性题目。1 是丑数,假设 x 是丑数,则 2x、3x、5x 也是丑数,所以我们依次加入堆中,第 n 次出队时,对应元素即为第 n 个丑数。

最小堆

#!/usr/bin/python
# -*- coding: UTF-8 -*-class Solution(object):def nthUglyNumber(self, n):""":type n: int:rtype: int"""nums = [2, 3, 5]# 去重appeared = {1}# 将初始化丑数放到队列里pq = [1]# 每次从队列取最小值,然后将对应数 2x 3x 5x 入队for i in range(1, n+1):x = heapq.heappop(pq)if i == n:return xfor num in nums:t = num * xif t not in appeared:appeared.add(t)heapq.heappush(pq, t)return -1

三指针

class Solution(object):def nthUglyNumber(self, n):if n < 0:return 0dp = [1] * n# 维护3个指针index2, index3, index5 = 0, 0, 0for i in range(1, n):# 每次看哪个位置乘出来的丑数最小dp[i] = min(2 * dp[index2], 3 * dp[index3], 5 * dp[index5])# 用过的索引 += 1if dp[i] == 2 * dp[index2]: index2 += 1if dp[i] == 3 * dp[index3]: index3 += 1if dp[i] == 5 * dp[index5]: index5 += 1return dp[n - 1]

4.Top-K-Freq-Ele [347]

前 k 个高频元素: https://leetcode.cn/problems/top-k-frequent-elements/description/

题目分析 

这题找 k 个最大,还是使用堆即可,只不过需要预先做一次 word_count 统计词频,这里如果是 scala 可以直接用 tuple-2 再 sortBy(-_.2) 即可,python 的话就直接使用 heqpq.nlargest 即可。

最大堆 API

class Solution(object):def topKFrequent(self, nums, k):""":type nums: List[int]:type k: int:rtype: List[int]"""if k == 0:return []# 1.构造 word count 计数count = {}for i in nums:if i not in count:count[i] = 0count[i] += 1# 2.构造 Heapheap = [(val, key) for key, val in count.items()]# 3.取前 k 个元素return [item[1] for item in heapq.nlargest(k, heap)]

这里使用 nlargest 获取前 k 个元素,但是我们构造时使用了全部数据,下面我们自己维护 k 个元素实现 heap。

K-最大堆

class Solution(object):def topKFrequent(self, nums, k):""":type nums: List[int]:type k: int:rtype: List[int]"""if k == 0:return []# 1.构造 word count 计数count = {}for i in nums:if i not in count:count[i] = 0count[i] += 1# 2.构造 Heapre = []heapq.heapify(re)for key, val in count.items():# 添加元素heapq.heappush(re, (val, key))# 超过 k 就把堆顶最小的拿走,最后剩下 K 个最大的while len(re) > k:heapq.heappop(re)return [i[1] for i in re]

 因为我们只维护了 k 大小的堆,遍历数组是 o(n),而堆内排序是 o(logk),所以时间快一些。

四.总结

本文结合上一篇文章的二叉树介绍了堆的概念以及常用的堆的功能应用,取前 k 个最大 or 最小的问题适合用堆实现,其次常用的二叉堆我们应该记住父子节点之间的关系。最后是常用堆的实现代码,这里我们都是调用 heapq 库,如果想要自己了解可以参考下述链接: 

Heap Sort – Data Structures and Algorithms Tutorials

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

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

相关文章

如何 0 改造,让单体/微服务应用成为 Serverless Application

简介&#xff1a; 随着 2013 年以 Docker 为代表的容器技术、CNCF 基金会以及 K8s 的发展等&#xff0c;云原生开始被广大开发者所熟知。云原生时代之前还有两个阶段&#xff1a;一是自建 IDC 机房&#xff0c;二是简单地把原有的应用搬迁到云上。自建 IDC 机房很难获得高可用、…

一文了解阿里一站式图计算平台GraphScope

简介&#xff1a; 随着大数据的爆发&#xff0c;图数据的应用规模不断增长&#xff0c;现有的图计算系统仍然存在一定的局限。阿里巴巴拥有全球最大的商品知识图谱&#xff0c;在丰富的图场景和真实应用的驱动下&#xff0c;阿里巴巴达摩院智能计算实验室研发并开源了全球首个一…

c++如何禁用指定的键盘布局_Karabiner Elements for Mac 键盘键位自定义改键工具

文章来源于&#xff1a;风云社区Karabiner Elements for Mac 12.5Karabiner Elements&#xff08;早期是Karabiner&#xff0c;更早是KeyRemap4MacBook&#xff09;是功能强大且稳定的macOS键盘定制器。上【风云社区】&#xff0c;搜索软件名字&#xff0c;即可查看下载特征&am…

Docker Desktop 向大公司宣告收费,网友大呼:是时候弃用了!

作者 | 苏宓 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 在容器引擎 Docker 诞生的 8 年间&#xff0c;其与开源的容器编排 Kubernetes 共同推动容器技术在云计算领域的应用&#xff0c;也让自身在全球范围内受到了广泛的关注。可以说&#xff0c;做过云计算开…

如何接地气地接入微前端?

简介&#xff1a; 微前端带来明显好处的同时&#xff0c;也面临着痛点。对于已有站点&#xff0c;如何在老的技术栈基础上接入一个微前端&#xff1f;需要哪些通 一 、前言 微前端&#xff0c;这个概念已经在国内不止一次的登上各大热门话题&#xff0c;它所解决的问题也很明显…

东南亚再造天猫 Lazada品牌商城LazMall举办第二届品牌未来论坛

9月1日&#xff0c;东南亚领先的旗舰电商平台Lazada在新加坡滨海湾金沙会展中心举办了2021 LazMall Brands Future Forum年度品牌未来论坛&#xff08;以下简称“BFF”&#xff09;。该论坛以“奔向未来&#xff1a;东南亚的数字商务时代”为主题&#xff0c;在庆祝Lazada品牌商…

高可用的本质

简介&#xff1a; 无论是一个域&#xff0c;一个BG&#xff0c;还是一个站点&#xff0c;虽然范围有大有小&#xff0c;对象有所不同&#xff0c;但其高可用的理念都是相通的&#xff0c;今天将自己对高可用的一点点思考以及总结的【nPRT公式】分享给大家。 我是乐羊&#xff0…

技术干货 | 深度解构 Android 应用面临紧急发版时的救星方案:mPaaS 热修复——DexPatch

简介&#xff1a; 关于 Android 热修复方案——DexPatch 的介绍与使用说明 方案介绍 为了解决 Native 模块上线后的问题&#xff0c;mPaaS 提供了热修复功能&#xff0c;实现不发布客户端 apk 场景下的热修复。目前 Android 端热修复主要包括 andfix 和 dexpatch&#xff0c;考…

李飞飞:阿里云数据库已做好全面服务政企市场的准备

“政企市场是检验云数据库产品竞争力的黄金标准。”9月3日&#xff0c;阿里云智能数据库事业部总负责人李飞飞在北京举办的媒体沟通会上表示&#xff0c;阿里云已经做好全面服务政企数据库市场的准备&#xff0c;并已成功助力多家大型组织实现核心系统对传统商业数据库的替换。…

技术改变生活 浅谈阿里云混合云的探索与实践

简介&#xff1a; 也许你并不了解“阿里云混合云”&#xff0c;甚至没有听说过“混合云”&#xff0c;然而它却在幕后“默默”改变着人们的生活。 也许你并不了解“阿里云混合云”&#xff0c;甚至没有听说过“混合云”&#xff0c;然而它却在幕后“默默”改变着人们的生活。大…

公网访问_一文读懂阿里云访问公网的实现方式

NAT网关与EIP作为公有云服务商&#xff0c;提供互联网的访问和接入是必备的条件&#xff0c;阿里云也不例外。和AWS类似&#xff0c;阿里云访问公网的组件为NAT网关和弹性IP&#xff0c;对于刚刚接触云的童鞋&#xff0c;今天这篇文章带你彻底了解这两个组件的使用场景。弹性IP…

阿里巴巴云原生应用安全防护实践与 OpenKruise 的新领域

简介&#xff1a; 得益于 Kubernetes 面向终态的理念&#xff0c;云原生架构天然具备高度自动化的能力。然而&#xff0c;面向终态的自动化是一把“双刃剑”&#xff0c;它既为应用带来了声明式的部署能力&#xff0c;同时也潜在地会将一些误操作行为被终态化放大。 因此&#…

什么是微内核架构设计?

简介&#xff1a; 作为一名Java程序员&#xff0c;相信同学们都听说过微内核架构设计&#xff0c;也有自己的理解。那么微内核是如何被提出来的&#xff1f;微内核在操作系统内核的设计中又有什么作用&#xff1f;本文从插件化(Plug-in)架构的角度来诠释微内核架构设计&#xf…

给力!斩获 GitHub 14000 Star,两周创办开源公司获数百万美元融资

上世纪 90 年代初&#xff0c;21 岁大学生 Linus Torvalds 开源 Linux 操作系统&#xff0c;自此掀起全球开源浪潮。随后“中国 Linux 第一人”宫敏博士用手提肩背的方式将 20 盒磁带背回中国&#xff0c;磁带里装着 80G 容量的自由软件&#xff0c;组建起中国第一个自由软件库…

函数计算镜像加速:从分钟到秒的跨越

简介&#xff1a; 函数计算 FC 正式发布容器镜像加速&#xff0c;通过按需读取和更高效的解压技术在不同场景下加速 50%-80%&#xff0c;即使 GB 级别的镜像也可以在几秒内完成端到端启动。 FaaS 和容器 容器镜像因其颠覆式创新成为云原生时代应用部署格式的事实标准。头部云厂…

阿里云CDN产品经理陈章炜:边缘创新技术和落地实践

简介&#xff1a; CDN除了加速外&#xff0c;不断被赋予更多价值。在阿里云CDN推出的《极速奔跑吧 2021》首场直播中&#xff0c;阿里云架构师和产品经理不仅对近期阿里云发布的CDN产品最佳实践图进行了详细解读&#xff0c;还对CDN产品和客户的场景如何更高效地匹配、形成最优…

极狐GitLab:从硅谷到中国,远程办公背后的挑战与创新

编辑 | 宋 慧 供稿 | 极狐&#xff08;GitLab&#xff09; 头图 | 付费下载于视觉中国 最近&#xff0c;海外的互联网巨头们纷纷开启了远程办公的政策&#xff0c;谷歌允许员工提出更换办公地点的要求或申请成为永久远程办公者&#xff0c;目前已经批准了近 8000 名员工在家办公…

E百科 | 基于MEC的边缘AI服务

简介&#xff1a; 阿里云边缘计算团队付哲解读5G下热门场景&#xff1a;边缘AI。作者&#xff1a;阿里云付哲&#xff0c;计算机科学与技术专业博士后&#xff0c;在流量检测、资源调度领域有深入研究&#xff0c;其论文《Astraea: Deploy AI Services at the Edge in Elegant …

网速dns怎么调快_怎么设置dns?教你快速解决网速慢的问题

体内惊人荒之力很的洪&#xff0c;设置速解速慢设置速解速慢了一但其大批企业高成中中隐藏优质长的&#xff0c;复活&#xff0c;一旦。特斯提高金拉2的第度为的产大量的资7年能投入了三季&#xff0c;教决网仅生0辆产了&#xff0c;理想并不其效果却&#xff0c;交付2辆只有实…

“凡尔赛”式晒校园生活?移动云 9.9 风暴手把手教你!

快开学了卧虎藏龙的校园当然也少不了“凡尔赛大师”看看普通版和进阶版的凡尔赛学霸学神们如何用最低调的话炫最高调的耀LETS GO!考/试/篇假/期/篇生/活/费/篇看完学霸与学神的凡尔赛较量除了羡慕他们能够凡尔赛的资本更重要的是了解到移动云校园套餐原来如&#xff01;此&…