Python中的模块heapq以及使用方法详解

heapq模块在Python中提供了堆数据结构的操作,包括nlargest和nsmallest函数,用于找出列表中n个最大/最小的元素。这两个函数支持使用key参数处理复杂数据结构。heapify函数可以将普通列表转换为堆,便于使用heappush、heappop等高效操作。文章还介绍了如何利用heapq实现一个简单的优先级队列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python中的 heapq 模块

1、heapq 的两个函数:nlargest() 和 nsmallest()

1.1 nlargest(n, iterable, key=None) 函数

功能:获取可迭代对象iterable中n个最大的元素,返回这n个最大的元素列表(该列表从最大到小排列)

  • 示例代码1:
import heapq

arr_list = [5, 595, 2, 19, 5, 68, 4, 165, 46, 16]
print(heapq.nlargest(3, arr_list))

# 打印结果 [595, 165, 68]
# 
  • 示例代码2(参数key的用法):
"""
    key 的用法是,对于这种列表套字典的数据,根据字典的某个键,取出该数据的n个这个键的最大的n个数据
"""
import heapq

data_list = [
    {"name": "python", "price": 90},
    {"name": "java", "price": 34},
    {"name": "c", "price": 60},
    {"name": "html", "price": 70},
    {"name": "go", "price": 83}
]

price_max_3 = heapq.nlargest(3, data_list, key=lambda x: x['price'])
print(price_max_3)

# 打印结果 [{'name': 'python', 'price': 90}, {'name': 'go', 'price': 83}, {'name': 'html', 'price': 70}]

1.2 nsmallest(n, iterable, key=None) 函数

功能:获取可迭代对象iterable中n个最小的元素,返回这n个最小的元素列表(该列表从最小到大排列)

  • 示例代码1:
import heapq

arr_list = [5, 595, 2, 19, 5, 68, 4, 165, 46, 16]
print(heapq.nsmallest(3, arr_list))

# 打印结果 [2, 4, 5]
  • 示例代码2(参数key的用法):
"""
    key 的用法是,对于这种列表套字典的数据,根据字典的某个键,取出该数据的n个这个键的最小的n个数据
"""
import heapq

data_list = [
    {"name": "python", "price": 90},
    {"name": "java", "price": 34},
    {"name": "c", "price": 60},
    {"name": "html", "price": 70},
    {"name": "go", "price": 83}
]

# price_max_3 = heapq.nlargest(3, data_list, key=lambda x: x['price'])
price_min_3 = heapq.nsmallest(3, data_list, key=lambda x: x['price'])
print(price_min_3)

# 打印结果 [{'name': 'java', 'price': 34}, {'name': 'c', 'price': 60}, {'name': 'html', 'price': 70}]

2、heapq 的 heapify()函数

heapify() 函数将普通的列表转换为堆

heapify() 函数通过将普通列表转换为堆,使得我们可以使用堆中提供的一些高效的操作,
例如 heappush()、heappop()、heapreplace() 等函数,
这些函数能够在常数时间内插入元素、弹出最小元素、替换最小元素。
这使得堆成为一种非常高效的数据结构,在诸多算法中得以广泛应用。

heapify() 函数将普通的列表转换为堆的过程如下:
1、从列表的末尾开始,依次向前遍历所有元素。对于每个元素,将其与它的父节点进行比较。如果该元素比其父节点小(或大,如果是大根堆),则将它与其父节点交换。
2、继续向前遍历,对于每个元素,重复上述比较和交换过程,直至整个列表变成一个堆。
3、最终得到的堆中,第一个元素是堆中最小的元素(或最大的元素,如果是大根堆)。


大根堆和小根堆是堆(Heap)这种数据结构的两种常见实现方式。它们的区别如下:
    大根堆:在大根堆中,每个节点的值都大于等于其子节点的值,堆中最大的元素位于堆的根节点。
    小根堆:在小根堆中,每个节点的值都小于等于其子节点的值,堆中最小的元素位于堆的根节点。
在一般的堆排序算法中,都使用小根堆来实现堆。这是因为小根堆的根节点是堆中最小的元素,可以方便地弹出并加入一个序列,从而完成排序。当然,如果需要寻找序列中最大的元素,也可以使用大根堆实现。
  • 示例代码:
import heapq

arr_list = [5, 595, 2, 19, 5, 68, 4, 165, -20, 46, 16]
heapq.heapify(arr_list)  # 默认是转化为小根堆
print("默认的小根堆:", arr_list)
arr_list = [-x for x in arr_list]  # 取相反数,转换为大根堆
heapq.heapify(arr_list)  # 转换为大根堆
arr_list = [-x for x in arr_list]
print("取反后的大根堆:", arr_list)

# 打印结果
# 默认的小根堆: [-20, 5, 2, 19, 5, 68, 4, 165, 595, 46, 16]
# 取反后的大根堆: [595, 165, 68, 19, 46, 2, 4, 5, -20, 5, 16]

2、heapq 的 heappop()函数

将第一个元素(最小的)弹出,然后以第二小的元素取而代之,复杂度为O(logN), N代表堆的大小

import heapq

arr_list = [5, 595, 2, 19, 5, 68, 4, 165, -20, 46, 16]
heapq.heapify(arr_list)  # 默认是转化为小根堆
print(heapq.heappop(arr_list))  # -20
print(heapq.heappop(arr_list))  # 2
print(heapq.heappop(arr_list))  # 4
print(heapq.heappop(arr_list))  # 5

3. heapq 的 heappush()函数

heapq.heappush(heap, item)

用于将一个元素添加到一个堆中,并保持堆的不变性。
这个函数接受两个参数:
    heap:表示要添加元素的堆。堆可以是一个列表,也可以是一个支持类似于列表的接口的对象。
    item:要添加的元素。
heappush() 函数将要添加的元素插入堆中,并保持堆的不变性。插入的元素必须是可比较的对象,因为堆中的元素必须可以进行排序。默认情况下,heappush() 函数会按照元素的大小进行排序,因此通常要求元素是数字类型或实现了比较操作的自定义对象。
  • 示例代码:
import heapq

# 创建一个空堆
heap = []

# 添加一些元素到堆中
heapq.heappush(heap, 3)
heapq.heappush(heap, 1)
heapq.heappush(heap, 4)
heapq.heappush(heap, 1)

# 查看堆中的元素
print(heap)

# 打印结果
# [1, 1, 4, 3]
# 使用 heappush() 函数向一个空堆 heap 中添加了四个元素。
# 由于 heappush() 函数会按照元素的大小进行排序,因此添加的元素在堆中被自动排序了。
# 最后,我们打印出堆中的元素,结果为 [1, 1, 4, 3]。可以看到,堆中的元素已经按照升序排列,
# 并且包含了多个相同元素。

4. 使用 heapq 模块实现一个简单的优先级队列

import heapq


class PriorityQueue:
    """
        heapq 模块实现的简单的优先级队列
    """

    def __init__(self):
        self._queue = []
        self._index = 0

    def push(self, item, priority):
        # 把 priority 取负值是未来让队列能够按元素的优先级从高到低的顺序排列
        heapq.heappush(self._queue, (-priority, self._index, item))
        self._index += 1

    def pop(self):
        return heapq.heappop(self._queue)[-1]


class Item:

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return "Item({!r})".format(self.name)


q = PriorityQueue()
# 往队列里面推数据
q.push(Item(name='python'), 5)  # 设置name为python,优先级为1
q.push(Item(name='java'), 1)  # 设置name为python,优先级为1
q.push(Item(name='c'), 1)  # 设置name为python,优先级为1
q.push(Item(name='vue'), 3)  # 设置name为python,优先级为1
q.push(Item(name='go'), 2)  # 设置name为python,优先级为1

# 从队列里面取出数据,按照设置的优先级,取出数据
print(q.pop())  # Item('python')
print(q.pop())  # Item('vue')
print(q.pop())  # Item('go')
print(q.pop())  # Item('java')
### Python 中 `heapq.heapify` 的用法与实现细节 #### 1. 函数定义 `heapq.heapify` 是 Python 标准库中的一个函数,用于将列表原地转换成堆结构。它的时间复杂度为 O(n),其中 n 表示列表的长度[^2]。 #### 2. 堆的概念 堆是一种特殊的完全二叉树数据结构,在最小堆中父节点总是小于或等于其子节点;而在最大堆中则相反。Python 的 `heapq` 模块默认实现了最小堆的功能[^3]。 #### 3. 使用方法 调用此函数非常简单,只需传入一个可变序列即可完成操作: ```python import heapq lst = [5, 7, 9, 1, 3] heapq.heapify(lst) print(lst) # 输出: [1, 3, 9, 7, 5] ``` 以上代码展示了如何利用 `heapq.heapify` 将普通列表转化为堆形式[^4]。 #### 4. 实现原理 虽然官方文档并未公开具体算法流程图,但从时间复杂度分析可以推测其实现基于自底向上的调整策略(Sift-down approach)[^5]。对于每一个非叶子结点执行下沉(sink down)动作直到满足整个数组成为合法的小顶堆为止。 以下是模拟该过程的一个简化版本: ```python def _siftup(heap, pos): endpos = len(heap) startpos = pos newitem = heap[pos] # Bubble up the smaller child until hitting a leaf. childpos = 2*pos + 1 # leftmost child position while childpos < endpos: # Set childpos to index of smaller child. rightpos = childpos + 1 if rightpos < endpos and not heap[childpos] < heap[rightpos]: childpos = rightpos # Move the smaller child up. heap[pos] = heap[childpos] pos = childpos childpos = 2*pos + 1 # The leaf at pos is empty now. Put newitem there, and bubble it up # to its final resting place (by sifting its parents down). heap[pos] = newitem _siftdown(heap, startpos, pos) def _siftdown(heap, startpos, pos): newitem = heap[pos] # Follow the path to the root, moving parents down until finding a place newitem fits. while pos > startpos: parentpos = (pos - 1) >> 1 parent = heap[parentpos] if newitem < parent: heap[pos] = parent pos = parentpos continue break heap[pos] = newitem def my_heapify(x): """Transform list into a heap, in-place.""" n = len(x) # Start from last non-leaf node and sift down each one. for i in reversed(range(n//2)): _siftup(x,i) # Example Usage example_list=[8,5,6,3,2,1] my_heapify(example_list) print(example_list) #[1, 2, 6, 3, 5, 8] ``` 上述代码片段通过 `_siftup` 和 `_siftdown` 方法手动构建了一个类似于 CPython 解释器内部使用的堆化逻辑[^6]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还是那个同伟伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值