python-数据结构-堆-heapq模块

堆的定义:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。

(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4...n/2)

 

堆是一种重要的线性数据结构,通常被看作是一棵树的数组对象。堆总是一个完全二叉树

由于二叉树良好的形态已经包含了父节点和孩子节点的关系信息,因此就可以不使用链表而简单的使用数组来存储堆。

 

Python没有独立的堆类型,而只有一个包含一些堆操作函数的模块: heapq模块

heapq模块提供了堆队列算法的实现,也称为优先队列算法。

堆是完全二叉树,且每个父节点的值都小于或等于它的任何子节点,heapq模块的堆算法算法实现的数组heap中,对于所有的索引值(0~n-1)都满足  heap[k] <= heap[2*k+1]  且 heap[k] <= heap[2*k+2] 。 为了便于比较,不存在的元素被认为是无限的。

这个模块有关的API与教科书中描述的堆算法有两个不同之处:

     (a)使用基于零的索引。这使得节点的索引与其子节点的索引之间的关系稍微不那么明显,但是更适合,因为Python使用基于零的索引。

     (b)pop方法只返回最小的项;它最小的元素总是根元素heap[0](min heap)

 

 1. 函数heappush用于在堆中添加一个元素。

注意,不能将它用于普通列表,而只能用于使用各种堆函数创建的列表。原因是元素的顺序很重要

如果使用普通列表,不报错但是结果不对的。

 

 2. 函数heappop弹出最小的元素(总是位于索引0处),并确保剩余元素中最小的那个位于索引0处(保持堆特征)。虽然弹出列表中第一个元素的效率通常不是很高,但这不是问题,因为heappop会在幕后做些巧妙的移位操作。

 

3.函数heapify通过执行尽可能少的移位操作将列表变成合法的堆(具备堆特征)

 

4. 函数heapreplace 它从堆中弹出最小的元素,再压入一个新元素。相比于依次执行函数heappop和heappush,这个函数的效率更高。

 

5. nlargest(n, iterable, key=None)和nsmallest(n, iterable, key=None):分别用于找出可迭代对象iterable中最大和最小的n个元素

 

注意: 如果需要重复使用这些函数,可以考虑将iterable转换为实际的堆。

 

 

查找最大或最小的 N 个元素?

 

当要查找的元素个数相对比较小的时候,函数 nlargest() 和 nsmallest() 是很合适的。

如果你仅仅想查找唯一的最小或最大(N=1)的元素的话,那么使用 min() 和max() 函数会更快些。

如果 N 的大小和集合大小接近的时候,通常先排序这个集合然后再使用切片操作会更快点(sorted(items)[:N] 或者是 sorted(items)[-N:]

 

 

堆的应用:海量实数中(一亿级别以上)找到TopK(一万级别以下)的数集合。

  • A 排序:通常遇到找一个集合中的TopK问题,想到的便是排序,因为常见的排序算法例如快排算是比较快了,然后再取出K个TopK数,时间复杂度为O(nlogn),当n很大的时候这个时间复杂度还是很大的;  对于一亿数据来说,A方案大约是26.575424*n

  • B 堆队列:由于我们只需要TopK,因此不需要对所有数据进行排序,可以利用堆的思想,维护一个大小为K的小顶堆,然后依次遍历每个元素e, 若元素e大于堆顶元素root,则删除root,将e放在堆顶,然后调整为小顶堆,时间复杂度为logK;这样遍历一遍后,最小堆里面保留的数就是我们要找的topK,整体时间复杂度为O(k+n*logk)约等于O(n*logk),大约是13.287712*n(由于k与n数量级差太多),这样时间复杂度下降了约一半。在python的nlargest(n, iterable, key=None)的实现中实现了这种算法:


if key is None:

        it = iter(iterable)

        # put the range(n) first so that zip() doesn't

        # consume one too many elements from the iterator

        result = [(elem, i) for i, elem in zip(range(n), it)]

        if not result:

            return result

        _heapify_max(result)

        top = result[0][0]

        order = n

        _heapreplace = _heapreplace_max

        for elem in it:

            if elem < top:

                _heapreplace(result, (elem, order))

                top = result[0][0]

                order += 1

        result.sort()

        return [r[0] for r in result]

 

A、B、两个方案中,当Kn的数量级相差越大,B方式越有效。


https://www.itcodemonkey.com/article/2082.html《堆和堆的应用》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章