patternpythonModerate
Implementing heap in Python
Viewed 0 times
implementingpythonheap
Problem
This is basically a straightforward heap implementation. I am just moving from C to Python and I wanted to make sure that I follow Python's best practices in general. This heap is supposed to support data from any data type with any comparing function.
```
class Heap(object):
""""
Attributes:
heap: List representation of the heap
compar(p, c): comparator function, returns true if the relation between p and c is parent-chield
"""
def __init__(self, compar):
self.heap = []
self.compar = compar
def is_empty(self):
return len(self.heap) == 0
def _inv_heapify(self, element_id):
"""
Do heapifying starting from bottom till it reaches the root.
"""
while element_id > 0:
if self.compar(self.heap[element_id / 2], self.heap[element_id]):
return
self.heap[element_id / 2], self.heap[element_id] = self.heap[element_id], self.heap[element_id / 2]
element_id /=2
def _heapify(self, element_id):
"""
Do heepifying starting from the root.
"""
l = len(self.heap)
if l == 1:
return
while 2 * element_id < l:
el_id = 2 * element_id
if 2 element_id + 1 < l and self.compar(self.heap[element_id 2 + 1], self.heap[element_id * 2]):
el_id += 1
if self.compar(self.heap[element_id], self.heap[el_id]):
return
self.heap[element_id], self.heap[el_id] = self.heap[el_id], self.heap[element_id]
element_id = el_id
def del_min(self):
if self.is_empty():
return None
x = self.heap.pop(0)
if not self.is_empty():
self.heap = [self.heap[-1]] + self.heap[0:-1]
self._heapify (0)
return x
def min(self):
if self.is_empty():
return None
return self.heap[0]
def add(self, element):
self.heap +=[elemen
```
class Heap(object):
""""
Attributes:
heap: List representation of the heap
compar(p, c): comparator function, returns true if the relation between p and c is parent-chield
"""
def __init__(self, compar):
self.heap = []
self.compar = compar
def is_empty(self):
return len(self.heap) == 0
def _inv_heapify(self, element_id):
"""
Do heapifying starting from bottom till it reaches the root.
"""
while element_id > 0:
if self.compar(self.heap[element_id / 2], self.heap[element_id]):
return
self.heap[element_id / 2], self.heap[element_id] = self.heap[element_id], self.heap[element_id / 2]
element_id /=2
def _heapify(self, element_id):
"""
Do heepifying starting from the root.
"""
l = len(self.heap)
if l == 1:
return
while 2 * element_id < l:
el_id = 2 * element_id
if 2 element_id + 1 < l and self.compar(self.heap[element_id 2 + 1], self.heap[element_id * 2]):
el_id += 1
if self.compar(self.heap[element_id], self.heap[el_id]):
return
self.heap[element_id], self.heap[el_id] = self.heap[el_id], self.heap[element_id]
element_id = el_id
def del_min(self):
if self.is_empty():
return None
x = self.heap.pop(0)
if not self.is_empty():
self.heap = [self.heap[-1]] + self.heap[0:-1]
self._heapify (0)
return x
def min(self):
if self.is_empty():
return None
return self.heap[0]
def add(self, element):
self.heap +=[elemen
Solution
You have a couple of PEP8 style problems:
You also have a couple of naming problems:
Other things I'd change:
Combining the above together gets you:
Rather than implementing this yourself, you can use Pythons
Since it's not a class you can easily make it one by wrapping it in one. But it doesn't have your custom comparisons, it is instead always a min heap.
If you need the custom comparisons, you could instead look into writing your own comparison object that does what you want, and use the heap class.
- Functions should have an empty line above and below them.
- Function calls should have their bracket immediately after the name. So
fn(notfn (.
- Assignment operators should have a space either side of them.
- Try to keep code less than 80 characters long.
You also have a couple of naming problems:
comparshould becompareorcomparer, since compar is not a word. If you wanted to shorten itcompwould be the best shortened version, but is worse than both the written out versions.
- Lists and arrays don't have IDs, they have indexes. And so
element_idis confusing. At first I useditem_indexorelement_index, but decided to instead useparentandchildto better describe the parent child relationship.
- In
heapifyI'd changeel_idtochildandelement_idtoparent. (And you should usechildrather thanparent * 2)
- For better readability, and for a minor performance boost, I used
heap = self.heap.
Other things I'd change:
- Your constructor shouldn't need to be passed
compare, and so it could default tooperator.lt. You may also want to take a heap as input, but you may need to add more code so it works correctly.
- Add a
__repr__, so that you can more easily tell what the object is.
- In
del_minwhen you add the two lists, it runs in \$O(n)\$ time. Where you can do the same withheap.pop(), which runs in \$O(1)\$ time.
- You may want to look at
heapq's source code to find other things you can do. It for example uses;_siftup, and_siftdown, and;_siftup_max, and_siftdown_max. Where you only write two of these.
Combining the above together gets you:
import operator
class Heap(object):
""""
Attributes:
heap: List representation of the heap
compare(p, c): comparator function, returns true if the relation between p and c is parent-chield
"""
def __init__(self, heap=None, compare=operator.lt):
self.heap = [] if heap is None else heap
self.compare = compare
def __repr__(self):
return 'Heap({!r}, {!r})'.format(self.heap, self.compare)
def _inv_heapify(self, child_index):
"""
Do heapifying starting from bottom till it reaches the root.
"""
heap, compare = self.heap, self.compare
child = child_index
while child > 0:
parent = child // 2
if compare(heap[parent], heap[child]):
return
heap[parent], heap[child] = heap[child], heap[parent]
child = parent
def _heapify(self, parent_index):
"""
Do heepifying starting from the root.
"""
heap, compare = self.heap, self.compare
length = len(heap)
if length == 1:
return
parent = parent_index
while 2 * parent < length:
child = 2 * parent
if child + 1 < length and compare(heap[child + 1], heap[child]):
child += 1
if compare(heap[parent], heap[child]):
return
heap[parent], heap[child] = heap[child], heap[parent]
parent = child
def del_min(self):
heap = self.heap
last_element = heap.pop()
if not heap:
return last_element
item = heap[0]
heap[0] = last_element
self._heapify(0)
return item
def min(self):
if not self.heap:
return None
return self.heap[0]
def add(self, element):
self.heap.append(element)
self._inv_heapify(len(self.heap) - 1)Rather than implementing this yourself, you can use Pythons
heapq, which may be written in C.Since it's not a class you can easily make it one by wrapping it in one. But it doesn't have your custom comparisons, it is instead always a min heap.
If you need the custom comparisons, you could instead look into writing your own comparison object that does what you want, and use the heap class.
class Heap(list):
def __init__(self, heap=None):
if heap is None:
heap = []
heapq.heapify(heap)
super(Heap, self).__init__(heap)
def __repr__(self):
return 'Heap({})'.format(super(Heap, self).__repr__())
def push(self, item):
return heapq.heappush(self, item)
def heappop(self):
return heapq.heappop(self)
def pushpop(self, item):
return heapq.heappushpop(self, item)
def replace(self, item):
return heapq.heapreplace(self, item)
def nlargest(self, n, *args, **kwargs):
return heapq.nlargest(n, self, *args, **kwargs)
def nsmallest(self, n, *args, **kwargs):
return heapq.nsmallest(n, self, *args, **kwargs)Code Snippets
import operator
class Heap(object):
""""
Attributes:
heap: List representation of the heap
compare(p, c): comparator function, returns true if the relation between p and c is parent-chield
"""
def __init__(self, heap=None, compare=operator.lt):
self.heap = [] if heap is None else heap
self.compare = compare
def __repr__(self):
return 'Heap({!r}, {!r})'.format(self.heap, self.compare)
def _inv_heapify(self, child_index):
"""
Do heapifying starting from bottom till it reaches the root.
"""
heap, compare = self.heap, self.compare
child = child_index
while child > 0:
parent = child // 2
if compare(heap[parent], heap[child]):
return
heap[parent], heap[child] = heap[child], heap[parent]
child = parent
def _heapify(self, parent_index):
"""
Do heepifying starting from the root.
"""
heap, compare = self.heap, self.compare
length = len(heap)
if length == 1:
return
parent = parent_index
while 2 * parent < length:
child = 2 * parent
if child + 1 < length and compare(heap[child + 1], heap[child]):
child += 1
if compare(heap[parent], heap[child]):
return
heap[parent], heap[child] = heap[child], heap[parent]
parent = child
def del_min(self):
heap = self.heap
last_element = heap.pop()
if not heap:
return last_element
item = heap[0]
heap[0] = last_element
self._heapify(0)
return item
def min(self):
if not self.heap:
return None
return self.heap[0]
def add(self, element):
self.heap.append(element)
self._inv_heapify(len(self.heap) - 1)class Heap(list):
def __init__(self, heap=None):
if heap is None:
heap = []
heapq.heapify(heap)
super(Heap, self).__init__(heap)
def __repr__(self):
return 'Heap({})'.format(super(Heap, self).__repr__())
def push(self, item):
return heapq.heappush(self, item)
def heappop(self):
return heapq.heappop(self)
def pushpop(self, item):
return heapq.heappushpop(self, item)
def replace(self, item):
return heapq.heapreplace(self, item)
def nlargest(self, n, *args, **kwargs):
return heapq.nlargest(n, self, *args, **kwargs)
def nsmallest(self, n, *args, **kwargs):
return heapq.nsmallest(n, self, *args, **kwargs)Context
StackExchange Code Review Q#156027, answer score: 14
Revisions (0)
No revisions yet.