comparison planemo/lib/python3.7/site-packages/networkx/utils/mapped_queue.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
1 # -*- coding: utf-8 -*-
2 #
3 # priorityq: An object-oriented priority queue with updatable priorities.
4 #
5 # Copyright 2018 Edward L. Platt
6 #
7 # This file is part of NetworkX
8 #
9 # NetworkX is distributed under a BSD license; see LICENSE.txt for more
10 # information.
11 #
12 # Authors:
13 # Edward L. Platt <ed@elplatt.com>
14 #
15 """Priority queue class with updatable priorities.
16 """
17
18 import heapq
19
20 __all__ = ['MappedQueue']
21
22
23 class MappedQueue(object):
24 """The MappedQueue class implements an efficient minimum heap. The
25 smallest element can be popped in O(1) time, new elements can be pushed
26 in O(log n) time, and any element can be removed or updated in O(log n)
27 time. The queue cannot contain duplicate elements and an attempt to push an
28 element already in the queue will have no effect.
29
30 MappedQueue complements the heapq package from the python standard
31 library. While MappedQueue is designed for maximum compatibility with
32 heapq, it has slightly different functionality.
33
34 Examples
35 --------
36
37 A `MappedQueue` can be created empty or optionally given an array of
38 initial elements. Calling `push()` will add an element and calling `pop()`
39 will remove and return the smallest element.
40
41 >>> q = MappedQueue([916, 50, 4609, 493, 237])
42 >>> q.push(1310)
43 True
44 >>> x = [q.pop() for i in range(len(q.h))]
45 >>> x
46 [50, 237, 493, 916, 1310, 4609]
47
48 Elements can also be updated or removed from anywhere in the queue.
49
50 >>> q = MappedQueue([916, 50, 4609, 493, 237])
51 >>> q.remove(493)
52 >>> q.update(237, 1117)
53 >>> x = [q.pop() for i in range(len(q.h))]
54 >>> x
55 [50, 916, 1117, 4609]
56
57 References
58 ----------
59 .. [1] Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2001).
60 Introduction to algorithms second edition.
61 .. [2] Knuth, D. E. (1997). The art of computer programming (Vol. 3).
62 Pearson Education.
63 """
64
65 def __init__(self, data=[]):
66 """Priority queue class with updatable priorities.
67 """
68 self.h = list(data)
69 self.d = dict()
70 self._heapify()
71
72 def __len__(self):
73 return len(self.h)
74
75 def _heapify(self):
76 """Restore heap invariant and recalculate map."""
77 heapq.heapify(self.h)
78 self.d = dict([(elt, pos) for pos, elt in enumerate(self.h)])
79 if len(self.h) != len(self.d):
80 raise AssertionError("Heap contains duplicate elements")
81
82 def push(self, elt):
83 """Add an element to the queue."""
84 # If element is already in queue, do nothing
85 if elt in self.d:
86 return False
87 # Add element to heap and dict
88 pos = len(self.h)
89 self.h.append(elt)
90 self.d[elt] = pos
91 # Restore invariant by sifting down
92 self._siftdown(pos)
93 return True
94
95 def pop(self):
96 """Remove and return the smallest element in the queue."""
97 # Remove smallest element
98 elt = self.h[0]
99 del self.d[elt]
100 # If elt is last item, remove and return
101 if len(self.h) == 1:
102 self.h.pop()
103 return elt
104 # Replace root with last element
105 last = self.h.pop()
106 self.h[0] = last
107 self.d[last] = 0
108 # Restore invariant by sifting up, then down
109 pos = self._siftup(0)
110 self._siftdown(pos)
111 # Return smallest element
112 return elt
113
114 def update(self, elt, new):
115 """Replace an element in the queue with a new one."""
116 # Replace
117 pos = self.d[elt]
118 self.h[pos] = new
119 del self.d[elt]
120 self.d[new] = pos
121 # Restore invariant by sifting up, then down
122 pos = self._siftup(pos)
123 self._siftdown(pos)
124
125 def remove(self, elt):
126 """Remove an element from the queue."""
127 # Find and remove element
128 try:
129 pos = self.d[elt]
130 del self.d[elt]
131 except KeyError:
132 # Not in queue
133 raise
134 # If elt is last item, remove and return
135 if pos == len(self.h) - 1:
136 self.h.pop()
137 return
138 # Replace elt with last element
139 last = self.h.pop()
140 self.h[pos] = last
141 self.d[last] = pos
142 # Restore invariant by sifting up, then down
143 pos = self._siftup(pos)
144 self._siftdown(pos)
145
146 def _siftup(self, pos):
147 """Move element at pos down to a leaf by repeatedly moving the smaller
148 child up."""
149 h, d = self.h, self.d
150 elt = h[pos]
151 # Continue until element is in a leaf
152 end_pos = len(h)
153 left_pos = (pos << 1) + 1
154 while left_pos < end_pos:
155 # Left child is guaranteed to exist by loop predicate
156 left = h[left_pos]
157 try:
158 right_pos = left_pos + 1
159 right = h[right_pos]
160 # Out-of-place, swap with left unless right is smaller
161 if right < left:
162 h[pos], h[right_pos] = right, elt
163 pos, right_pos = right_pos, pos
164 d[elt], d[right] = pos, right_pos
165 else:
166 h[pos], h[left_pos] = left, elt
167 pos, left_pos = left_pos, pos
168 d[elt], d[left] = pos, left_pos
169 except IndexError:
170 # Left leaf is the end of the heap, swap
171 h[pos], h[left_pos] = left, elt
172 pos, left_pos = left_pos, pos
173 d[elt], d[left] = pos, left_pos
174 # Update left_pos
175 left_pos = (pos << 1) + 1
176 return pos
177
178 def _siftdown(self, pos):
179 """Restore invariant by repeatedly replacing out-of-place element with
180 its parent."""
181 h, d = self.h, self.d
182 elt = h[pos]
183 # Continue until element is at root
184 while pos > 0:
185 parent_pos = (pos - 1) >> 1
186 parent = h[parent_pos]
187 if parent > elt:
188 # Swap out-of-place element with parent
189 h[parent_pos], h[pos] = elt, parent
190 parent_pos, pos = pos, parent_pos
191 d[elt] = pos
192 d[parent] = parent_pos
193 else:
194 # Invariant is satisfied
195 break
196 return pos