首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01|动态数组:按需分配的vector为什么要二倍扩容?
02|双向链表:list如何实现高效地插入与删除?
03|双端队列:并行计算中的工作窃取算法如何实现?
04|栈:函数调用的秘密究竟是什么?
05|HashMap:一个优秀的散列表是怎么来的?
06|TreeMap:红黑树真的有那么难吗?
07|堆:如何实现一个高效的优先队列?
08|外部排序:如何为TB级数据排序?
09|二分:如何高效查询Kafka中的消息?
10|搜索算法: 一起来写一个简单的爬虫?
11|字符串匹配:如何实现最快的grep工具
12|拓扑排序:Webpack是如何确定构建顺序的?
13|哈夫曼树:HTTP2.0是如何更快传输协议头的?
14|调度算法:操作系统中的进程是如何调度的?
15|LRU:在虚拟内存中页面是如何置换的?
16|日志型文件系统:写入文件的时候断电了会发生什么?
17|选路算法:Dijkstra是如何解决最短路问题的?
18|选路算法:链路状态算法是如何分发全局信息的
19|选路算法:距离矢量算法为什么会产生无穷计算问题?
20|滑动窗口:TCP是如何进行流量控制和拥塞控制的?
21|分而治之:MapReduce如何解决大规模分布式计算问题
22|PageRank:谷歌是如何计算网页排名的
23|Raft:分布式系统间如何达成共识?
24|UUID:如何高效生成全局的唯一ID?
25|一致性哈希:如何在集群上合理分配流量?
26|B+ Tree:PostgreSQL 的索引是如何建立的?
27|LSM Tree:LevelDB的索引是如何建立的?
28|MVCC:如何突破数据库并发读写性能瓶颈?
29|位图:如何用更少空间对大量数据进行去重和排序?
30|布隆过滤器:如何解决Redis缓存穿透问题?
31|跳表:Redis是如何存储有序集合的?
32|时间轮:Kafka是如何实现定时任务的?
33|限流算法:如何防止系统过载?
34|前缀树:Web框架中如何实现路由匹配?
当前位置:
首页>>
技术小册>>
业务开发实用算法精讲
小册名称:业务开发实用算法精讲
### 07|堆:如何实现一个高效的优先队列? 在软件开发和算法设计的广阔领域中,优先队列是一种至关重要的数据结构,它允许我们按照元素的优先级顺序进行访问,而非简单的先入先出(FIFO)或后入先出(LIFO)规则。优先队列广泛应用于任务调度、事件模拟、图算法(如Dijkstra算法和Prim算法)等多个领域。堆(Heap),特别是二叉堆,是实现优先队列的一种高效方式,通过维护堆的特定属性来保证每次都能快速访问到优先级最高的元素。 #### 一、优先队列的基本概念 优先队列是一种特殊的队列,其中每个元素都被赋予了一个优先级。在出队(或称为“删除”)操作中,具有最高优先级的元素总是被首先移除。根据优先级的定义,可以是数值上的最小或最大,这决定了是构建最小堆还是最大堆。 - **最小堆**:在最小堆中,父节点的值总是小于或等于其子节点的值。这意味着根节点(位于数组的第一个位置)是堆中的最小元素。 - **最大堆**:在最大堆中,父节点的值总是大于或等于其子节点的值。因此,根节点是堆中的最大元素。 #### 二、堆的实现原理 堆通常使用数组来存储元素,并利用数组的索引关系来模拟树形结构。对于数组中任意位置为`i`的元素(假设数组索引从0开始),其左子节点位于`2*i+1`,右子节点位于`2*i+2`,而父节点则位于`(i-1)/2`(向下取整)。这种映射关系使得堆的操作(如插入、删除等)能够高效地进行。 #### 三、堆的基本操作 ##### 1. 插入操作(上浮调整) 向堆中插入一个新元素时,通常将该元素添加到数组的末尾,然后通过一系列的比较和交换操作(称为上浮或上滤),将该元素移动到适当的位置,以保持堆的性质。 - **步骤**: 1. 将新元素添加到数组的末尾。 2. 从新元素开始,与其父节点比较。 3. 如果新元素小于其父节点(对于最小堆),则交换它们。 4. 重复步骤2和3,直到新元素达到其正确位置或成为根节点。 ##### 2. 删除根节点操作(下沉调整) 删除堆的根节点(即优先级最高或最低的元素)时,通常将数组的最后一个元素移动到根节点位置,然后通过下沉或下滤操作,重新调整堆的结构,以维持堆的性质。 - **步骤**: 1. 将数组的最后一个元素移动到根节点位置。 2. 从根节点开始,与其左右子节点中较小的(对于最小堆)进行比较。 3. 如果存在更小的子节点,则交换它们。 4. 重复步骤2和3,直到当前节点是叶子节点或满足堆的性质。 #### 四、堆的详细实现 以下是一个使用Python实现的最小堆(优先队列)的示例代码: ```python class MinHeap: def __init__(self): self.heap = [] def parent(self, i): return (i - 1) // 2 def left_child(self, i): return 2 * i + 1 def right_child(self, i): return 2 * i + 2 def has_parent(self, i): return self.parent(i) >= 0 def has_left_child(self, i): return self.left_child(i) < len(self.heap) def has_right_child(self, i): return self.right_child(i) < len(self.heap) def swap(self, i, j): self.heap[i], self.heap[j] = self.heap[j], self.heap[i] def insert(self, key): self.heap.append(key) self.heapify_up(len(self.heap) - 1) def heapify_up(self, index): while self.has_parent(index) and self.heap[self.parent(index)] > self.heap[index]: self.swap(self.parent(index), index) index = self.parent(index) def get_min(self): if not self.heap: return None return self.heap[0] def extract_min(self): if not self.heap: return None if len(self.heap) == 1: return self.heap.pop() root = self.heap[0] self.heap[0] = self.heap.pop() self.heapify_down(0) return root def heapify_down(self, index): smallest = index left = self.left_child(index) right = self.right_child(index) if self.has_left_child(index) and self.heap[left] < self.heap[smallest]: smallest = left if self.has_right_child(index) and self.heap[right] < self.heap[smallest]: smallest = right if smallest != index: self.swap(index, smallest) self.heapify_down(smallest) def peek(self): if not self.heap: return None return self.heap[0] def size(self): return len(self.heap) ``` #### 五、堆的性能分析 - **时间复杂度**: - 插入操作(上浮调整):平均和最坏情况下都是O(log n),其中n是堆中元素的数量。 - 删除根节点操作(下沉调整):同样,平均和最坏情况下都是O(log n)。 - 获取根节点(最小或最大元素):O(1)。 - **空间复杂度**:O(n),因为堆使用了一个数组来存储所有元素。 #### 六、堆的应用场景 堆作为优先队列的实现,广泛应用于多种算法和数据结构中: - **任务调度**:在操作系统或分布式系统中,使用堆来管理任务的优先级,确保高优先级的任务能够优先执行。 - **图算法**:如Dijkstra算法使用最小堆来维护待处理的节点集合,以便每次都能选择到当前最短路径估计最小的节点。 - **堆排序**:堆排序算法利用堆的性质进行排序,虽然堆本身不是排序算法,但它是堆排序算法的核心部分。 - **内存管理**:在某些内存管理策略中,使用堆来管理空闲内存块,以便快速找到足够大的内存块来满足分配请求。 #### 七、总结 堆作为一种高效的数据结构,通过维护特定的堆属性(最小堆或最大堆),实现了对元素的优先级排序。通过数组实现的堆,在插入和删除操作中能够保持O(log n)的时间复杂度,使得堆成为实现优先队列的理想选择。无论是理论研究还是实际应用,堆都展现出了其独特的魅力和广泛的应用价值。在编写算法或设计系统时,合理地利用堆结构,可以显著提升程序的性能和效率。
上一篇:
06|TreeMap:红黑树真的有那么难吗?
下一篇:
08|外部排序:如何为TB级数据排序?
该分类下的相关小册推荐:
编程之道-算法面试(上)
数据结构与算法(下)
编程之道-算法面试(下)
数据结构与算法(上)
算法面试通关 50 讲
数据结构与算法之美
数据结构与算法(中)