使用 heapq 降序
Descending order using heapq
我正在使用 Python 的 heapq 模块按升序和降序获取数据。
对于升序,我使用的是最小堆,效果很好,如下所示:
>>> from heapq import heapify, heappop
>>> heap = [9, 3, 1, 5, 6, 2, 7]
>>> heapify(heap)
>>> heappop(heap)
1
>>> heappop(heap)
2
>>> heappop(heap)
3
对于降序,我尝试了不同的方法,但它们都有一些缺点:
使用负值作为优先级进行反向排序。我必须使用单独的列表来使数据可重用。如果原始列表很大,则复制列表的成本很高。
>>> from heapq import heapify, heappop
>>> heap = [9, 3, 1, 5, 6, 2, 7]
>>> heap_neg = [-x for x in heap]
>>> heapify(heap_neg)
>>> -heappop(heap_neg)
9
>>> -heappop(heap_neg)
7
>>> -heappop(heap_neg)
6
优先使用负值元组,这也是浪费space。我不想将整数列表存储为元组列表。
>>> from heapq import heapify, heappop
>>> heap = [(-9, 9), (-3, 3), (-1, 1), (-5, 5), (-6, 6), (-2,2), (-7,7)]
>>> heapify(heap)
>>> heappop(heap)[1]
9
>>> heappop(heap)[1]
7
>>> heappop(heap)[1]
6
在 heapify 中缺少使用键排序。类似于:
>>> from heapq import heapify, heappop
>>> heap = [9, 3, 1, 5, 6, 2, 7]
>>> heapify(heap, key=lambda x:-x) # This doesn't work as heapify don't have key parameter
如果我使用heapq._heapify_max(堆),我将不得不在每个元素弹出后_heapify_max。喜欢:
>>> from heapq import _heapify_max, heappop
>>> heap = [9, 3, 1, 5, 6, 2, 7]
>>> _heapify_max(heap)
>>> heappop(heap)
9
>>> heappop(heap) # popping without _heapify_max gives wrong result
1
>>> _heapify_max(heap)
>>> heappop(heap) # popping after _heapify_max gives correct result
7
有什么方法可以让我获得 降序 顺序,类似于我获得 升序 顺序的方式? :)
我认为在这种情况下你正在寻找一个排序的链表,我修改了我找到的一个 here 所以它会按升序插入(我添加了 pop 函数,由于某种原因它不是' t 在代码中,但我认为您可能需要它):
# Python program to insert in sorted list
# Node class
class Node:
# Constructor to initialize the node object
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
# Function to initialize head
def __init__(self):
self.head = None
def sortedInsert(self, new_node):
# Special case for the empty linked list
if self.head is None:
new_node.next = self.head
self.head = new_node
# Special case for head at end
elif self.head.data <= new_node.data:
new_node.next = self.head
self.head = new_node
else :
# Locate the node before the point of insertion
current = self.head
while(current.next is not None and
current.next.data > new_node.data):
current = current.next
new_node.next = current.next
current.next = new_node
# Function to insert a new node at the beginning
def push(self, new_data):
new_node = Node(new_data)
new_node.next = self.head
self.head = new_node
# Utility function to prit the linked LinkedList
def printList(self):
temp = self.head
while(temp):
print(temp.data),
temp = temp.next
def pop(self):
val = self.head.data
self.head = self.head.next
return val
# Driver program
llist = LinkedList()
new_node = Node(5)
llist.sortedInsert(new_node)
new_node = Node(10)
llist.sortedInsert(new_node)
new_node = Node(7)
llist.sortedInsert(new_node)
new_node = Node(3)
llist.sortedInsert(new_node)
new_node = Node(1)
llist.sortedInsert(new_node)
new_node = Node(9)
llist.sortedInsert(new_node)
print("Create Linked List")
llist.printList()
如您所见,只是将 >= 更改为 <=,它完美地完成了工作
正如我们在评论中讨论的那样,当您从空堆开始并将值添加为你去。由于这是找到值流的 运行 中位数的用例,因此在添加值时取反应该就可以了。
这是我编写的 运行 中值生成器,只是为了仔细检查它是否按我预期的方式工作:
def running_median(iterable):
left_q = [] # heap of smaller-than-median elements, stored negated
right_q = [] # heap of larger-than-median elements
for value in iterable:
if len(left_q) == len(right_q): # push to left_q when they're equal size
if len(right_q) > 0 and value > right_q[0]:
value = heapq.heapreplace(right_q, value)
heapq.heappush(left_q, -value)
else: # push to right_q only when it's (strictly) smaller
if value < -left_q[0]:
value = -heapq.heapreplace(left_q, -value)
heapq.heappush(right_q, value)
# len(left_q) is always >= len(right_q) so we never yield right_q[0]
if len(left_q) > len(right_q):
yield -left_q[0]
else:
yield (-left_q[0] + right_q[0]) / 2
left_q
堆存储小于或等于中值的值。每个值在被压入时都会被取反,因此对它使用正常的最小堆操作可以使它像最大堆一样工作。我们只需要记住重新否定我们从中取出的任何值,回到原来的符号。
有私有方法(在 python 3.8 测试)
import heapq
if __name__ == '__main__':
a = [1, 3, 2, 5]
heapq._heapify_max(a)
for item in range(0, len(a)):
print(heapq._heappop_max(a)
结果是
sorted heap 5
sorted heap 3
sorted heap 2
sorted heap 1
但是对于某些人来说,使用私有方法可能看起来不够正确。出于这个原因,我们可以通过将您的对象放在修改后的包装器中来更改顺序
class DescOrder:
def __init__(self, entity):
self.entity = entity
def __lt__(self, o):
return self.entity.__gt__(o.entity)
def __repr__(self):
return str(self.entity)
def check_sorting(a, b):
new_heap = []
for element in a:
heapq.heappush(new_heap, DescOrder(element))
for index in range(0, len(b)):
assert heapq.heappop(new_heap).entity == b[index]
if __name__ == '__main__':
check_sorting([5, 1, -1, 3, 2], [5, 3, 2, 1, -1])
check_sorting([5, 2, -1, 3, 1], [5, 3, 2, 1, -1])
check_sorting([-1, 2, 5, 3, 1], [5, 3, 2, 1, -1])
我正在使用 Python 的 heapq 模块按升序和降序获取数据。
对于升序,我使用的是最小堆,效果很好,如下所示:
>>> from heapq import heapify, heappop
>>> heap = [9, 3, 1, 5, 6, 2, 7]
>>> heapify(heap)
>>> heappop(heap)
1
>>> heappop(heap)
2
>>> heappop(heap)
3
对于降序,我尝试了不同的方法,但它们都有一些缺点:
使用负值作为优先级进行反向排序。我必须使用单独的列表来使数据可重用。如果原始列表很大,则复制列表的成本很高。
>>> from heapq import heapify, heappop >>> heap = [9, 3, 1, 5, 6, 2, 7] >>> heap_neg = [-x for x in heap] >>> heapify(heap_neg) >>> -heappop(heap_neg) 9 >>> -heappop(heap_neg) 7 >>> -heappop(heap_neg) 6
优先使用负值元组,这也是浪费space。我不想将整数列表存储为元组列表。
>>> from heapq import heapify, heappop >>> heap = [(-9, 9), (-3, 3), (-1, 1), (-5, 5), (-6, 6), (-2,2), (-7,7)] >>> heapify(heap) >>> heappop(heap)[1] 9 >>> heappop(heap)[1] 7 >>> heappop(heap)[1] 6
在 heapify 中缺少使用键排序。类似于:
>>> from heapq import heapify, heappop >>> heap = [9, 3, 1, 5, 6, 2, 7] >>> heapify(heap, key=lambda x:-x) # This doesn't work as heapify don't have key parameter
如果我使用heapq._heapify_max(堆),我将不得不在每个元素弹出后_heapify_max。喜欢:
>>> from heapq import _heapify_max, heappop >>> heap = [9, 3, 1, 5, 6, 2, 7] >>> _heapify_max(heap) >>> heappop(heap) 9 >>> heappop(heap) # popping without _heapify_max gives wrong result 1 >>> _heapify_max(heap) >>> heappop(heap) # popping after _heapify_max gives correct result 7
有什么方法可以让我获得 降序 顺序,类似于我获得 升序 顺序的方式? :)
我认为在这种情况下你正在寻找一个排序的链表,我修改了我找到的一个 here 所以它会按升序插入(我添加了 pop 函数,由于某种原因它不是' t 在代码中,但我认为您可能需要它):
# Python program to insert in sorted list
# Node class
class Node:
# Constructor to initialize the node object
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
# Function to initialize head
def __init__(self):
self.head = None
def sortedInsert(self, new_node):
# Special case for the empty linked list
if self.head is None:
new_node.next = self.head
self.head = new_node
# Special case for head at end
elif self.head.data <= new_node.data:
new_node.next = self.head
self.head = new_node
else :
# Locate the node before the point of insertion
current = self.head
while(current.next is not None and
current.next.data > new_node.data):
current = current.next
new_node.next = current.next
current.next = new_node
# Function to insert a new node at the beginning
def push(self, new_data):
new_node = Node(new_data)
new_node.next = self.head
self.head = new_node
# Utility function to prit the linked LinkedList
def printList(self):
temp = self.head
while(temp):
print(temp.data),
temp = temp.next
def pop(self):
val = self.head.data
self.head = self.head.next
return val
# Driver program
llist = LinkedList()
new_node = Node(5)
llist.sortedInsert(new_node)
new_node = Node(10)
llist.sortedInsert(new_node)
new_node = Node(7)
llist.sortedInsert(new_node)
new_node = Node(3)
llist.sortedInsert(new_node)
new_node = Node(1)
llist.sortedInsert(new_node)
new_node = Node(9)
llist.sortedInsert(new_node)
print("Create Linked List")
llist.printList()
如您所见,只是将 >= 更改为 <=,它完美地完成了工作
正如我们在评论中讨论的那样,当您从空堆开始并将值添加为你去。由于这是找到值流的 运行 中位数的用例,因此在添加值时取反应该就可以了。
这是我编写的 运行 中值生成器,只是为了仔细检查它是否按我预期的方式工作:
def running_median(iterable):
left_q = [] # heap of smaller-than-median elements, stored negated
right_q = [] # heap of larger-than-median elements
for value in iterable:
if len(left_q) == len(right_q): # push to left_q when they're equal size
if len(right_q) > 0 and value > right_q[0]:
value = heapq.heapreplace(right_q, value)
heapq.heappush(left_q, -value)
else: # push to right_q only when it's (strictly) smaller
if value < -left_q[0]:
value = -heapq.heapreplace(left_q, -value)
heapq.heappush(right_q, value)
# len(left_q) is always >= len(right_q) so we never yield right_q[0]
if len(left_q) > len(right_q):
yield -left_q[0]
else:
yield (-left_q[0] + right_q[0]) / 2
left_q
堆存储小于或等于中值的值。每个值在被压入时都会被取反,因此对它使用正常的最小堆操作可以使它像最大堆一样工作。我们只需要记住重新否定我们从中取出的任何值,回到原来的符号。
有私有方法(在 python 3.8 测试)
import heapq
if __name__ == '__main__':
a = [1, 3, 2, 5]
heapq._heapify_max(a)
for item in range(0, len(a)):
print(heapq._heappop_max(a)
结果是
sorted heap 5
sorted heap 3
sorted heap 2
sorted heap 1
但是对于某些人来说,使用私有方法可能看起来不够正确。出于这个原因,我们可以通过将您的对象放在修改后的包装器中来更改顺序
class DescOrder:
def __init__(self, entity):
self.entity = entity
def __lt__(self, o):
return self.entity.__gt__(o.entity)
def __repr__(self):
return str(self.entity)
def check_sorting(a, b):
new_heap = []
for element in a:
heapq.heappush(new_heap, DescOrder(element))
for index in range(0, len(b)):
assert heapq.heappop(new_heap).entity == b[index]
if __name__ == '__main__':
check_sorting([5, 1, -1, 3, 2], [5, 3, 2, 1, -1])
check_sorting([5, 2, -1, 3, 1], [5, 3, 2, 1, -1])
check_sorting([-1, 2, 5, 3, 1], [5, 3, 2, 1, -1])