每日一题:最大堆的实现
很久没有做题目了,今天学习下最大堆和最小堆这种数据结构。主要涉及知识点: 1、如何构建一个最大(小)堆 2、如何获取最大(小)元素 3、实现获取无序数组中第k大的数字,对应leetcode:https://leetcode.com/problems/kth-largest-element-in-an-array/
coding…
文中均以最大堆为例,最小堆的原理类似
什么是最大堆
定义很简单: 1、它是一棵二叉树,并且是一棵完成二叉树 2、各个子树的根结点都比孩子结点要大,所以整棵树的根结点即为所有数中最大的那个数
堆的构建
这里我们采用数组来实现一个最大堆。为什么会选用数组呢,主要原因如下图所示:
用数组构建最大堆的构建两种构建方式,一种是循环插入,即一个一个插入,每次插入后的结点都保持最大堆的形式;而另外一种则是先把数据按数据顺序插入,然后从第一个叶子结点开始往上调整。
我们先来看第一种构建方式,分如下几步: 1、将当前插入的元素直接放至数组的末尾 2、从插入值开始往上找,如果父结点值比当前数值小,则交换,直到找到比他大的或者没有结点可找之后结束
实现代码如下:
class MaxHeap():
"""
最大堆
"""
def __init__(self):
self._count = 0
self._data = [0]
def shift_up(self, index):
while index > 1 and self._data[index // 2] < self._data[index]:
self._data[index // 2], self._data[index] = self._data[index], self._data[index // 2]
index //= 2
def insert(self, value):
self._data.append(value)
self._count += 1
self.shift_up(self._count)
def print(self):
print(self._data[1:])
if __name__ == "__main__":
heap = MaxHeap()
nums = [10, 20, 9, 4, 5, 30]
for num in nums:
heap.insert(num)
heap.print() # [30, 10, 20, 4, 5, 9]
接着我们来看第二种构建方式,实现步骤如下: 1、直接将整个数据填入数组中 2、从第一个非叶结点开始,向上走,每次与自己的左、右结点比较,调整位置,走到调整到根结点为止
实现代码如下:
class MaxHeap():
"""
最大堆
"""
def __init__(self, nums):
self._data = [0]
self._count = 0
for num in nums:
self._data.append(num)
self._count += 1
for i in range(self._count // 2, 0, -1):
self.shift_down(i)
def shift_down(self, index):
while index * 2 <= self._count:
m = index * 2
if m + 1 <= self._count and self._data[m + 1] > self._data[m]:
m = index * 2 + 1
if self._data[index] > self._data[m]:
break
self._data[index], self._data[m] = self._data[m], self._data[index]
index = m
def print(self):
print(self._data[1:])
if __name__ == "__main__":
nums = [10, 20, 9, 4, 5, 30]
heap = MaxHeap(nums)
heap.print()
两种实现方式复杂度不一样,第一种复杂度为 nO(logN),第二种为 O(n),具体分析原因这里不做解释,特别是第二种,需要通过一定的数学归纳方法来得到。可以暂时记忆为主。可以看到第二种的时间复杂度是要优于第一种的。
获取最大值
获取最大值,同样分两个步骤: 1、取出根结点 2、将最后一个结点与根结点交换,然后从根结点开始,与左右结点比较,直到符合条件
实现代码如下:
class MaxHeap():
"""
最大堆
"""
def __init__(self):
self._count = 0
self._data = [0]
def shift_up(self, index):
while index > 1 and self._data[index // 2] < self._data[index]:
self._data[index // 2], self._data[index] = self._data[index], self._data[index // 2]
index //= 2
def insert(self, value):
self._data.append(value)
self._count += 1
self.shift_up(self._count)
def print(self):
print(self._data[1:])
def extract_max(self):
cur_max = self._data[1]
self._data[1], self._data[self._count] = self._data[self._count], self._data[1]
self._count -= 1
self.shift_down(1)
return cur_max
def shift_down(self, index):
while index * 2 <= self._count:
m = index * 2
if m + 1 <= self._count and self._data[m + 1] > self._data[m]:
m = index * 2 + 1
if self._data[index] > self._data[m]:
break
self._data[index], self._data[m] = self._data[m], self._data[index]
index = m
if __name__ == "__main__":
heap = MaxHeap()
nums = [10, 20, 9, 4, 5, 30]
for num in nums:
heap.insert(num)
heap.print()
print(heap.extract_max())
实践
像我们平常遇到的查找第 n 项最大值就可以用最大堆来实现,如果用上面说的第二种构建方式,时间复杂度可优化为 O(n)。
这里直接上代码:
# https://leetcode.com/problems/kth-largest-element-in-an-array/
class MaxHeap():
"""
最大堆
"""
def __init__(self):
self._count = 0
self._data = [0]
def insert_arr(self, nums):
for num in nums:
self._data.append(num)
self._count += 1
for i in range(self._count // 2, 0, -1):
self.shift_down(i)
def shift_up(self, index):
while index > 1 and self._data[index // 2] < self._data[index]:
self._data[index // 2], self._data[index] = self._data[index], self._data[index // 2]
index //= 2
def shift_down(self, index):
while index * 2 <= self._count:
m = index * 2
if m + 1 <= self._count and self._data[m + 1] > self._data[m]:
m = index * 2 + 1
if self._data[index] > self._data[m]:
break
self._data[index], self._data[m] = self._data[m], self._data[index]
index = m
def insert(self, value):
self._data.append(value)
self._count += 1
self.shift_up(self._count)
def extract_max(self):
cur_max = self._data[1]
self._data[1], self._data[self._count] = self._data[self._count], self._data[1]
self._count -= 1
self.shift_down(1)
return cur_max
class Solution(object):
"""
实现思路:使用堆
"""
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
max_head = MaxHeap()
max_head.insert_arr(nums)
result = None
for i in range(k):
result = max_head.extract_max()
return result
if __name__ == "__main__":
s = Solution()
print(s.findKthLargest([3,2,3,1,2,4,5,5,6], 4))
- 零基础学编程031:Python与其它语言最不同的一条语法规则
- dedecms批量导出新增文章url和标题
- 明理知意:复合索引优化及索引访问原理
- WCF配置文件与文件下载之坎坷路
- 零基础学编程030:像黑客般玩玩字符艺术
- Android获取SD卡总容量,可用大小,机身内存总容量及可用大小
- 零基础学编程029:程序员作图不用笔
- 零基础学编程028:面向对象编程OOP
- 如何修改帝国cms文章点击量默认值和成倍增加
- 深入内核:监听器的工作原理与故障诊断分析
- 零基础学编程027:站在巨人的肩膀上
- 群分享:Markdown + CSS 实现微信公众号排版
- Android 蓝牙操作详解
- Linq to xml 操作带命名空间的xml
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android 自定义ListView实现QQ空间界面(说说内包含图片、视频、点赞、评论、转发功能)
- Android自定义View实现自动吸附功能
- Qt音视频开发35-Onvif图片参数
- alpine安装openssl
- iOS14中的PHPicker
- Android 如何实现动态申请权限
- Android录屏的三种解决方案
- Android 实现将Bitmap 保存到本地
- Android Gradle依赖管理、去除重复依赖、忽略的方式
- Andriod Studio实现保存QQ密码功能(案例代码详解)
- Android Studio编写微信页面提交功能
- android 实现按钮浮动在键盘上方的实例代码
- 创建Android守护进程实例(底层服务)
- android studio xml文件实现添加注释
- 解决Android studio xml界面无法预览问题