update utils.data_structures: add queues

This commit is contained in:
Brian Delhaisse
2019-04-22 17:21:17 +02:00
parent 062969be8d
commit 2088cd5e57
3 changed files with 405 additions and 27 deletions
@@ -4,5 +4,8 @@
# Ordered sets
from .orderedset import *
# Queues
from .queues import *
# Graph
from .graph import *
from .graph import *
+54 -26
View File
@@ -1,6 +1,19 @@
#!/usr/bin/env python
"""Define the OrderedSet data structure class.
"""
import collections
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
__credits__ = ["Brian Delhaisse"]
__license__ = "MIT"
__version__ = "1.0.0"
__maintainer__ = "Brian Delhaisse"
__email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class OrderedSet(collections.MutableSet):
r"""Ordered Set
@@ -59,6 +72,10 @@ class OrderedSet(collections.MutableSet):
for item in iterator:
self.add(item)
###########
# Methods #
###########
def add(self, item):
"""
Add/Append an item to the ordered set.
@@ -86,15 +103,14 @@ class OrderedSet(collections.MutableSet):
Time complexity: O(N)
"""
# check idx
idx = self._checkIndex(idx)
idx = self._check_index(idx)
if item in self._set:
# move the item at the specified location
self.move(idx, item) # O(N)
self.move(idx, item) # O(N)
else:
# add it
self._list.insert(idx, item) # O(N)
self._set.add(item) # O(1)
self._list.insert(idx, item) # O(N)
self._set.add(item) # O(1)
def move(self, idx, item):
"""
@@ -102,11 +118,10 @@ class OrderedSet(collections.MutableSet):
Time complexity: O(N)
"""
# remove the item from the list/set
self.remove(item) # O(N)
self.remove(item) # O(N)
# insert item
self.insert(idx, item) # O(N)
self.insert(idx, item) # O(N)
def discard(self, item):
"""
@@ -115,7 +130,7 @@ class OrderedSet(collections.MutableSet):
"""
if item in self._set:
self._list.remove(item) # O(N)
self._set.remove(item) # O(1)
self._set.remove(item) # O(1)
def remove(self, item):
"""
@@ -134,9 +149,9 @@ class OrderedSet(collections.MutableSet):
index: index in the ordered set.
"""
if index is None: index = len(self._list)
index = self._checkIndex(index) # to be sure the index is valid
self._set.remove(self._list[index]) # O(1)
item = self._list.pop(index) # O(1) if last, O(N) if first
index = self._check_index(index) # to be sure the index is valid
self._set.remove(self._list[index]) # O(1)
item = self._list.pop(index) # O(1) if last, O(N) if first
return item
def copy(self):
@@ -146,7 +161,7 @@ class OrderedSet(collections.MutableSet):
"""
return self.__class__(self)
def _checkIndex(self, idx):
def _check_index(self, idx):
"""
Check the given index; if it is in the range of the ordered set, and if it is negative return the
corresponding positive index.
@@ -274,7 +289,12 @@ class OrderedSet(collections.MutableSet):
"""
return other.issuperset(self, order=order)
#############
# Operators #
#############
def __repr__(self):
"""Return a representation string."""
return '%s(%r)' % (self.__class__.__name__, list(self))
def __contains__(self, item):
@@ -336,14 +356,14 @@ class OrderedSet(collections.MutableSet):
else:
self._list[idx] = item
self._set.add(item)
elif isinstance(idx, slice): # slice
elif isinstance(idx, slice): # slice
# replace in list
items_to_remove = self._list[idx] # O(K)
self._list[idx] = item # O(K+N)
items_to_remove = self._list[idx] # O(K)
self._list[idx] = item # O(K+N)
# remove previous items from the set
for elem in items_to_remove:
self._set.remove(elem) # O(1)
self._set.remove(elem) # O(1)
# add new items in the set
for elem in item:
@@ -455,6 +475,10 @@ class OrderedSet2(collections.MutableSet):
for item in iterator:
self.add(item)
###########
# Methods #
###########
def add(self, item):
"""
Add/Append an item to the ordered set.
@@ -491,7 +515,7 @@ class OrderedSet2(collections.MutableSet):
Time complexity: O(N)
"""
# check idx
idx = self._checkIndex(idx)
idx = self._check_index(idx)
# if the set is initially empty or index is the size of the set, just add the item (at the end)
if len(self._map) == 0 or idx == len(self._map):
@@ -503,10 +527,10 @@ class OrderedSet2(collections.MutableSet):
# get current item at the specified index, update the items nearby, and insert the new item
curr = self[idx]
prev_item, next_item = self._map[curr]
if prev_item == self.NonePtr: # beginning of the ordered set (idx=0)
if prev_item == self.NonePtr: # beginning of the ordered set (idx=0)
self._map[curr][0] = item
self._map[item] = [self.NonePtr, curr]
else: # somewhere between the start and the end (not included)
else: # somewhere between the start and the end (not included)
self._map[item] = [prev_item, next_item]
self._map[prev_item][1] = item
self._map[next_item][0] = item
@@ -525,7 +549,6 @@ class OrderedSet2(collections.MutableSet):
# insert item
self.insert(idx, item)
def discard(self, item):
"""
Remove an item from the ordered set if it is a member. If the item is not a member do nothing.
@@ -579,7 +602,7 @@ class OrderedSet2(collections.MutableSet):
"""
return self.__class__(self)
def _checkIndex(self, idx):
def _check_index(self, idx):
"""
Check the given index; if it is in the range of the ordered set, and if it is negative return the
corresponding positive index.
@@ -709,7 +732,12 @@ class OrderedSet2(collections.MutableSet):
"""
return other.issuperset(self, order=order)
#############
# Operators #
#############
def __repr__(self):
"""Return a representation string."""
return '%s(%r)' % (self.__class__.__name__, list(self))
def __contains__(self, item):
@@ -763,7 +791,7 @@ class OrderedSet2(collections.MutableSet):
if isinstance(idx, int): # index is an integer
# check index
idx = self._checkIndex(idx)
idx = self._check_index(idx)
# traverse the set in a specific order based on how close the index is wrt the start/end of the set
curr = self.NonePtr
@@ -818,7 +846,7 @@ class OrderedSet2(collections.MutableSet):
Time complexity: O(N)
"""
# check idx
idx = self._checkIndex(idx)
idx = self._check_index(idx)
# check if item already in the set
if item in self._map:
@@ -880,7 +908,7 @@ class OrderedSet2(collections.MutableSet):
self &= other
#OrderedSet = OrderedSet2
# OrderedSet = OrderedSet2
# Tests
@@ -954,4 +982,4 @@ if __name__ == '__main__':
print("s3 - s1 = {}".format(s3 - s1))
print("s3.difference(s1) = {}".format(s3.difference(s1)))
print("s1 - s2 = {}".format(s1 - s2))
print("s1 - s0 = {}".format(s1 - s0))
print("s1 - s0 = {}".format(s1 - s0))
+347
View File
@@ -0,0 +1,347 @@
#!/usr/bin/env python
"""Define the queue data structures.
These inherit from the various queues in the `queue` Python library, to which we provide few more functionalities.
"""
import queue
import heapq
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
__credits__ = ["Brian Delhaisse"]
__license__ = "MIT"
__version__ = "1.0.0"
__maintainer__ = "Brian Delhaisse"
__email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
# # add functionalities to the original queue.Queue (from which queue.PriorityQueue and LifoQueue inherit from)
#
# def append(self, item):
# """
# Append an item in at the end of the FIFO queue. If the queue is already full it will remove the first item.
# """
# # if full remove the first item
# if self.full():
# self.get()
#
# # add the new item
# self.put(item)
#
#
# def tolist(self):
# """
# Return a list of the queue.
# """
# return list(self.queue)
#
#
# def __repr__(self):
# """Return a representation string."""
# return str(self.queue)
#
#
# def __len__(self):
# """Return the current length of the FIFO queue."""
# return self.qsize()
#
#
# def __getitem__(self, idx):
# """Return the item corresponding to the given index.
#
# Args:
# idx (int): index.
# """
# return self.queue[idx]
#
#
# def __setitem__(self, idx, item):
# """Set the given item to the given index."""
# self.queue[idx] = item
#
#
# def __iter__(self):
# """Return an iterator over the queue."""
# return iter(self.queue)
#
#
# # add functionalities
# queue.Queue.append = append
# queue.Queue.tolist = tolist
# queue.Queue.__repr__ = __repr__
# queue.Queue.__len__ = __len__
# queue.Queue.__getitem__ = __getitem__
# queue.Queue.__setitem__ = __setitem__
# queue.Queue.__iter__ = __iter__
#
#
# # provide aliases
# FIFOQueue = queue.Queue
# LIFOQueue = queue.LifoQueue
class FIFOQueue(queue.Queue):
r"""FIFO Queue
We provide few more functionalities on top of the original `queue.Queue` class.
"""
def __init__(self, maxsize=0):
"""
Initialize the FIFO queue.
Args:
maxsize (int): maximum size of the queue.
"""
queue.Queue.__init__(self, maxsize)
def append(self, item):
"""
Append an item in at the end of the FIFO queue. If the queue is already full it will remove the first item.
"""
# if full remove the first item
if self.full():
self.get()
# add the new item
self.put(item)
def tolist(self):
"""
Return a list of the queue.
"""
return list(self.queue)
def __repr__(self):
"""Return a representation string."""
return str(self.queue)
def __len__(self):
"""Return the current length of the FIFO queue."""
return self.qsize()
def __getitem__(self, idx):
"""Return the item corresponding to the given index.
Args:
idx (int): index.
"""
return self.queue[idx]
def __setitem__(self, idx, item):
"""Set the given item to the given index."""
self.queue[idx] = item
def __iter__(self):
"""Return an iterator over the queue."""
return iter(self.queue)
class LIFOQueue(queue.LifoQueue):
r"""LIFO Queue.
We provide few more functionalities on top of the original `queue.LifoQueue` class.
"""
def __init__(self, maxsize=0):
"""
Initialize the FIFO queue.
Args:
maxsize (int): maximum size of the queue.
"""
queue.Queue.__init__(self, maxsize)
def append(self, item):
"""
Append an item in at the end of the FIFO queue. If the queue is already full it will remove the first item.
"""
# if full remove the first item
if self.full():
self.get()
# add the new item
self.put(item)
def tolist(self):
"""
Return a list of the queue.
"""
return list(self.queue)
def __repr__(self):
"""Return a representation string."""
return str(self.queue)
def __len__(self):
"""Return the current length of the FIFO queue."""
return self.qsize()
def __getitem__(self, idx):
"""Return the item corresponding to the given index.
Args:
idx (int): index.
"""
return self.queue[idx]
def __setitem__(self, idx, item):
"""Set the given item to the given index."""
self.queue[idx] = item
def __iter__(self):
"""Return an iterator over the queue."""
return reversed(self.queue)
class PriorityQueue(queue.PriorityQueue):
r"""Priority Queue.
We provide few more functionalities on top of the original `queue.PriorityQueue` class.
"""
def __init__(self, maxsize=0, ascending=True):
"""
Initialize the FIFO queue.
Args:
maxsize (int): maximum size of the queue.
ascending (bool): if True, the item with the lowest priority will be the first one to be retrieved.
"""
queue.Queue.__init__(self, maxsize)
self.ascending = bool(ascending)
def append(self, item):
"""
Append an item in at the end of the FIFO queue. If the queue is already full it will remove the first item.
"""
# if full remove the first item
if self.full():
self.get()
# add the new item
self.put(item)
def tolist(self):
"""
Return a list of the queue.
"""
return list(self.queue)
def _put(self, item, heappush=heapq.heappush):
if not self.ascending:
item = (-item[0], item[1])
queue.PriorityQueue._put(self, item, heappush=heappush)
def _get(self, heappop=heapq.heappop):
item = queue.PriorityQueue._get(self, heappop=heappop)
if not self.ascending:
item = (-item[0], item[1])
return item
def get_lowest(self):
"""
Get the lowest priority item.
"""
if len(self) == 0:
raise ValueError("The priority queue is empty.")
if self.ascending:
return self[0][1]
else:
return self[-1][1]
def get_highest(self):
"""
Get the highest priority item.
"""
if len(self) == 0:
raise ValueError("The priority queue is empty.")
if self.ascending:
return self[-1][1]
else:
return self[0][1]
def __repr__(self):
"""Return a representation string."""
return str(self.queue)
def __len__(self):
"""Return the current length of the FIFO queue."""
return self.qsize()
def __getitem__(self, idx):
"""Return the item corresponding to the given index.
Args:
idx (int): index.
"""
return self.queue[idx]
def __setitem__(self, idx, item):
"""Set the given item to the given index."""
self.queue[idx] = item
def __iter__(self):
"""Return an iterator over the queue."""
if self.ascending:
return iter(self.queue)
else:
return reversed(self.queue)
# Tests
if __name__ == '__main__':
# create queue
q = FIFOQueue(2)
print("Initial queue (maxsize={}): {}".format(q.maxsize, q))
# add two elements in the queue
q.append(1)
q.append(2)
print("After adding '1' and '2' in queue: {}".format(q))
# add third element in the queue
q.append(3)
print("After adding '3' in queue: {}".format(q))
# iterate over queue
for i, item in enumerate(q):
print("Item {}: {}".format(i, item))
# create stack
stack = LIFOQueue(maxsize=2)
print("\nInitial stack (maxsize={}): {}".format(stack.maxsize, stack))
# add two elements in the stack
stack.append(1)
stack.append(2)
print("After adding '1' and '2' in the stack: {}".format(stack))
# add third element
stack.append(3)
print("After adding '3' in queue: {}".format(stack))
# iterate over stack
for i, item in enumerate(stack):
print("Item {}: {}".format(i, item))
# create priority queue
pq = PriorityQueue(maxsize=2, ascending=True)
print("\nInitial priority queue (maxsize={}): {}".format(pq.maxsize, pq))
# add two elements in the queue
pq.append((1, 'hello'))
pq.append((2, 'world'))
print("After adding (1, 'hello') and (2, 'world') in priority queue: {}".format(pq))
print("Lowest priority item: {}".format(pq.get_lowest()))
print("Highest priority item: {}".format(pq.get_highest()))
print("Remove the 1st item: {} --> resulting priority queue: {}".format(pq.get(), pq))
# add two elements in the queue
pq.append((3, 'hello'))
pq.append((4, 'sir'))
print("After adding (3, 'hello') and (4, 'sir') in priority queue: {}".format(pq))