堆(大根堆、小根堆)

堆是一种特殊的树结构, 能够快速定位最大值或最小值, 是实现堆排序, 优先队列的关键,同时优先队列主要应用在 事件处理 和 任务调度 等场景。

定义

在Python中,堆分为大根堆和小根堆,它们本质上都是一棵完全二叉树,只是它们的节点大小关系不同:

  • 大根堆:对于任意节点 i,节点 i 的值不小于它的任何子节点的值,即父节点的值大于或等于子节点的值。它使用了数组来实现:从零开始计数,对于所有的 k ,都有 heap [k] >= heap [2k+1] 和 heap [k] >= heap [2k+2]
  • 小根堆:对于任意节点 i,节点 i 的值不大于它的任何子节点的值,即父节点的值小于或等于子节点的值。它使用了数组来实现:从零开始计数,对于所有的 k ,都有 heap [k] <= heap [2k+1] 和 heap [k] <= heap [2k+2]

大根堆和小根堆在实现上是类似的,唯一的区别就是插入的时候需要加上符号(大根堆为负,小根堆为正),取出元素时再次加上符号即可。使用 heapq 模块,只需要将插入堆中的元素加上负号就可以将大根堆变成小根堆,然后通过 heappop 函数弹出堆顶元素(也就是最小的元素),即可实现弹出堆内的最大元素。

import heapq

# 小根堆
min_heap = []
heapq.heappush(min_heap, 1)
heapq.heappush(min_heap, 2)
heapq.heappush(min_heap, 3)

# 输出结果为 [1, 2, 3]
print(min_heap)

# 大根堆
max_heap = []
for num in [1, 2, 3]:
    heapq.heappush(max_heap, -num) 	# 	注意负号!!!

# 输出结果为 [-3, -2, -1]
print(max_heap)

# 从大根堆中取出最大的元素
print(-heapq.heappop(max_heap))  # 输出 3   # 	注意负号!!!

实现

在 Python 中,我们可以使用内置的 heapq 模块来实现堆操作,它默认实现的是小根堆。如果需要实现大根堆,只需要将插入堆中的元素取相反数即可。

Python的heapq模块提供了一些堆操作的函数。其中最常用的是:

  • heappush(heap, item):将元素item加入堆heap中
  • heappop(heap):将堆heap中最小的元素弹出并返回,堆发生改变。
  • heapify(x):将列表x转换成堆。
  • heapreplace(heap, item):弹出堆heap中最小的元素,并将元素item加入堆中。
  • nlargest(n, iterable, key=None):返回iterable中值最大的n个元素,可选参数key用于指定排序依据。
  • nsmallest(n, iterable, key=None):返回iterable中值最小的n个元素,可选参数key用于指定排序依据。

应用

堆的应用:堆的应用非常广泛,比如在优先队列、排序、图算法中均有应用。在Python中,我们可以使用heapq模块中的函数来解决这些问题。下面是一些例子:
(1)使用堆排序

import heapq

nums = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]

# 将列表转换成小根堆
heapq.heapify(nums)

# 按照从小到大的顺序弹出堆中的元素
sorted_nums = []
while nums:
    sorted_nums.append(heapq.heappop(nums))

print(sorted_nums)

(2)使用堆实现优先队列
这是一个简单的优先队列的实现,其中每个元素都有一个对应的优先级。我们可以使用push函数将元素添加到队列中,使用pop函数弹出优先级最高的元素。注意,这里使用了一个_index变量来确保在优先级相等的情况下,先加入队列的元素先被弹出。

import heapq

class PriorityQueue:

	#	始化了一个空的列表 heap 和一个变量 index,作为优先队列的底层数据结构和索引值。
    def __init__(self):
        self._heap = []
        self._index = 0
        
	#	在 push 方法中,将元素 item 和优先级 priority 加入到 heap 中,并为了保持小根堆的性质,将优先级 priority 按照相反数加入到堆中(即插入 -priority)。同时,为了确保后续插入的元素按照插入的顺序排序,将 index 作为第二个元素加入到堆中。最后,将插入的元素以元组的形式加入到堆中。
    def push(self, item, priority):
        heapq.heappush(self._heap, (-priority, self._index, item))
        self._index += 1
        
	# 在 pop 方法中,弹出堆顶元素(也就是优先级最高的元素),并返回元组中的最后一个元素(也就是 item)。
    def pop(self):
        return heapq.heappop(self._heap)[-1]

(3)使用堆实现最小生成树

import heapq

def prim(graph):
    visited = set()
    edges = []
    start_vertex = next(iter(graph))
    visited.add(start_vertex)

    for neighbor, weight in graph[start_vertex].items():
        heapq.heappush(edges, (weight, start_vertex, neighbor))

    mst = []
    while edges:
        weight, u, v = heapq.heappop(edges)
        if v not in visited:
            visited.add(v)
            mst.append((u, v, weight))
            for neighbor, weight in graph[v].items():
                if neighbor not in visited:
                    heapq.heappush(edges, (weight, v, neighbor))

    return mst

你可能感兴趣的:(python,开发语言)