Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added more implementations of priority queue #205

Merged
merged 2 commits into from
Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 81 additions & 12 deletions pydatastructs/miscellaneous_data_structures/queue.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from pydatastructs.linear_data_structures import DynamicOneDimensionalArray, SinglyLinkedList
from pydatastructs.utils.misc_util import NoneType, LinkedListNode, _check_type
from pydatastructs.trees.heaps import BinaryHeap, BinomialHeap
from copy import deepcopy as dc

__all__ = [
Expand Down Expand Up @@ -186,13 +187,17 @@ class PriorityQueue(object):
of priority queue.
The following implementations are supported,
'linked_list' -> Linked list implementation.
Optional, by default, 'linked_list' implementation
'binary_heap' -> Binary heap implementation.
'binomial_heap' -> Binomial heap implementation.
Doesn't support custom comparators, minimum
key data is extracted in every pop.
Optional, by default, 'binary_heap' implementation
is used.
comp: function
The comparator to be used while comparing priorities.
Must return a bool object.
By default, `lambda u, v: u > v` is used to compare
priorities i.e., maximum priority elements are extracted
By default, `lambda u, v: u < v` is used to compare
priorities i.e., minimum priority elements are extracted
by pop operation.

Examples
Expand All @@ -203,43 +208,65 @@ class PriorityQueue(object):
>>> pq.push(1, 2)
>>> pq.push(2, 3)
>>> pq.pop()
2
>>> pq2 = PriorityQueue(comp=lambda u, v: u < v)
1
>>> pq2 = PriorityQueue(comp=lambda u, v: u > v)
>>> pq2.push(1, 2)
>>> pq2.push(2, 3)
>>> pq2.pop()
1
2

References
==========

.. [1] https://en.wikipedia.org/wiki/Priority_queue#Naive_implementations
.. [1] https://en.wikipedia.org/wiki/Priority_queue
"""

def __new__(cls, implementation='linked_list', **kwargs):
def __new__(cls, implementation='binary_heap', **kwargs):
comp = kwargs.get("comp", lambda u, v: u < v)
if implementation == 'linked_list':
return LinkedListPriorityQueue(
kwargs.get("comp", lambda u, v: u > v)
)
return LinkedListPriorityQueue(comp)
elif implementation == 'binary_heap':
return BinaryHeapPriorityQueue(comp)
elif implementation == 'binomial_heap':
return BinomialHeapPriorityQueue()
else:
raise NotImplementedError(
"%s implementation is not currently supported "
"by priority queue.")

def push(self, value, priority):
"""
Pushes the value to the priority queue
according to the given priority.

value
Value to be pushed.
priority
Priority to be given to the value.
"""
raise NotImplementedError(
"This is an abstract method.")

def pop(self):
"""
Pops out the value from the priority queue.
"""
raise NotImplementedError(
"This is an abstract method.")

@property
def is_empty(self):
"""
Checks if the priority queue is empty.
"""
raise NotImplementedError(
"This is an abstract method.")

class LinkedListPriorityQueue(PriorityQueue):

__slots__ = ['items', 'comp']

def __new__(cls, comp=lambda u, v: u > v):
def __new__(cls, comp):
obj = object.__new__(cls)
obj.items = SinglyLinkedList()
obj.comp = comp
Expand All @@ -266,3 +293,45 @@ def pop(self):
@property
def is_empty(self):
return self.items.size == 0

class BinaryHeapPriorityQueue(PriorityQueue):

__slots__ = ['items']

def __new__(cls, comp):
obj = object.__new__(cls)
obj.items = BinaryHeap()
obj.items._comp = comp
return obj

def push(self, value, priority):
self.items.insert(priority, value)

def pop(self):
node = self.items.extract()
return node.data

@property
def is_empty(self):
return self.items.is_empty

class BinomialHeapPriorityQueue(PriorityQueue):

__slots__ = ['items']

def __new__(cls):
obj = object.__new__(cls)
obj.items = BinomialHeap()
return obj

def push(self, value, priority):
self.items.insert(priority, value)

def pop(self):
node = self.items.find_minimum()
self.items.delete_minimum()
return node.data

@property
def is_empty(self):
return self.items.is_empty
21 changes: 12 additions & 9 deletions pydatastructs/miscellaneous_data_structures/tests/test_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,15 @@ def test_PriorityQueue():
assert _check_type(pq1, LinkedListPriorityQueue) is True
assert raises(NotImplementedError, lambda: Queue(implementation=''))

def test_LinkedListPriorityQueue():
pq1 = PriorityQueue(implementation='linked_list')
pq1.push(1, 2)
pq1.push(2, 3)
pq1.push(3, 4)
assert pq1.pop() == 3
assert pq1.pop() == 2
assert pq1.pop() == 1
assert raises(IndexError, lambda: pq1.pop())
def test_ImplementationPriorityQueue():
impls = ['linked_list', 'binomial_heap', 'binary_heap']
for impl in impls:
pq1 = PriorityQueue(implementation=impl)
pq1.push(1, 2)
pq1.push(2, 3)
pq1.push(3, 4)
assert pq1.pop() == 1
assert pq1.pop() == 2
assert pq1.pop() == 3
assert pq1.is_empty is True
assert raises(IndexError, lambda: pq1.pop())
11 changes: 9 additions & 2 deletions pydatastructs/trees/heaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def extract(self):
If the heap is empty.
"""
if self._last_pos_filled == -1:
return None
raise IndexError("Heap is empty.")
else:
element_to_be_extracted = TreeNode(self.heap[0].key, self.heap[0].data)
self._swap(0, self._last_pos_filled)
Expand All @@ -194,6 +194,13 @@ def __str__(self):
to_be_printed[i] = (node.key, node.data, children)
return str(to_be_printed)

@property
def is_empty(self):
"""
Checks if the heap is empty.
"""
return self.heap._last_pos_filled == -1


class BinaryHeap(DHeap):
"""
Expand Down Expand Up @@ -456,7 +463,7 @@ def find_minimum(self, **kwargs):
min_node: BinomialTreeNode
"""
if self.is_empty:
raise ValueError("Binomial heap is empty.")
raise IndexError("Binomial heap is empty.")
min_node = None
idx, min_idx = 0, None
for tree in self.root_list:
Expand Down
8 changes: 4 additions & 4 deletions pydatastructs/trees/tests/test_heaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def test_BinaryHeap():

max_heap = BinaryHeap(heap_property="max")

assert max_heap.extract() is None
assert raises(IndexError, lambda: max_heap.extract())

max_heap.insert(100, 100)
max_heap.insert(19, 19)
Expand Down Expand Up @@ -57,7 +57,7 @@ def test_BinaryHeap():
BinaryHeap(elements = non_TreeNode_elements, heap_property='min'))
def test_TernaryHeap():
max_heap = TernaryHeap(heap_property="max")
assert max_heap.extract() is None
assert raises(IndexError, lambda: max_heap.extract())
max_heap.insert(100, 100)
max_heap.insert(19, 19)
max_heap.insert(36, 36)
Expand Down Expand Up @@ -97,7 +97,7 @@ def test_TernaryHeap():
def test_DHeap():
assert raises(ValueError, lambda: DHeap(heap_property="none", d=4))
max_heap = DHeap(heap_property="max", d=5)
assert max_heap.extract() is None
assert raises(IndexError, lambda: max_heap.extract())
max_heap.insert(100, 100)
max_heap.insert(19, 19)
max_heap.insert(36, 36)
Expand Down Expand Up @@ -213,7 +213,7 @@ def bfs(heap):

# Testing BinomialHeap.insert
heap = BinomialHeap()
assert raises(ValueError, lambda: heap.find_minimum())
assert raises(IndexError, lambda: heap.find_minimum())
heap.insert(1, 1)
heap.insert(3, 3)
heap.insert(6, 6)
Expand Down