mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-24 05:21:09 +00:00
55ee273419
* fix the edge case of the double ended queue pop the last element * refactoring doc --------- Co-authored-by: Jiang15 <weijiang@weijiangs-MacBook-Pro.local>
463 lines
14 KiB
Python
463 lines
14 KiB
Python
"""
|
|
Implementation of double ended queue.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Iterable
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
|
|
class Deque:
|
|
"""
|
|
Deque data structure.
|
|
Operations
|
|
----------
|
|
append(val: Any) -> None
|
|
appendleft(val: Any) -> None
|
|
extend(iterable: Iterable) -> None
|
|
extendleft(iterable: Iterable) -> None
|
|
pop() -> Any
|
|
popleft() -> Any
|
|
Observers
|
|
---------
|
|
is_empty() -> bool
|
|
Attributes
|
|
----------
|
|
_front: _Node
|
|
front of the deque a.k.a. the first element
|
|
_back: _Node
|
|
back of the element a.k.a. the last element
|
|
_len: int
|
|
the number of nodes
|
|
"""
|
|
|
|
__slots__ = ("_front", "_back", "_len")
|
|
|
|
@dataclass
|
|
class _Node:
|
|
"""
|
|
Representation of a node.
|
|
Contains a value and a pointer to the next node as well as to the previous one.
|
|
"""
|
|
|
|
val: Any = None
|
|
next_node: Deque._Node | None = None
|
|
prev_node: Deque._Node | None = None
|
|
|
|
class _Iterator:
|
|
"""
|
|
Helper class for iteration. Will be used to implement iteration.
|
|
Attributes
|
|
----------
|
|
_cur: _Node
|
|
the current node of the iteration.
|
|
"""
|
|
|
|
__slots__ = ("_cur",)
|
|
|
|
def __init__(self, cur: Deque._Node | None) -> None:
|
|
self._cur = cur
|
|
|
|
def __iter__(self) -> Deque._Iterator:
|
|
"""
|
|
>>> our_deque = Deque([1, 2, 3])
|
|
>>> iterator = iter(our_deque)
|
|
"""
|
|
return self
|
|
|
|
def __next__(self) -> Any:
|
|
"""
|
|
>>> our_deque = Deque([1, 2, 3])
|
|
>>> iterator = iter(our_deque)
|
|
>>> next(iterator)
|
|
1
|
|
>>> next(iterator)
|
|
2
|
|
>>> next(iterator)
|
|
3
|
|
"""
|
|
if self._cur is None:
|
|
# finished iterating
|
|
raise StopIteration
|
|
val = self._cur.val
|
|
self._cur = self._cur.next_node
|
|
|
|
return val
|
|
|
|
def __init__(self, iterable: Iterable[Any] | None = None) -> None:
|
|
self._front: Any = None
|
|
self._back: Any = None
|
|
self._len: int = 0
|
|
|
|
if iterable is not None:
|
|
# append every value to the deque
|
|
for val in iterable:
|
|
self.append(val)
|
|
|
|
def append(self, val: Any) -> None:
|
|
"""
|
|
Adds val to the end of the deque.
|
|
Time complexity: O(1)
|
|
>>> our_deque_1 = Deque([1, 2, 3])
|
|
>>> our_deque_1.append(4)
|
|
>>> our_deque_1
|
|
[1, 2, 3, 4]
|
|
>>> our_deque_2 = Deque('ab')
|
|
>>> our_deque_2.append('c')
|
|
>>> our_deque_2
|
|
['a', 'b', 'c']
|
|
>>> from collections import deque
|
|
>>> deque_collections_1 = deque([1, 2, 3])
|
|
>>> deque_collections_1.append(4)
|
|
>>> deque_collections_1
|
|
deque([1, 2, 3, 4])
|
|
>>> deque_collections_2 = deque('ab')
|
|
>>> deque_collections_2.append('c')
|
|
>>> deque_collections_2
|
|
deque(['a', 'b', 'c'])
|
|
>>> list(our_deque_1) == list(deque_collections_1)
|
|
True
|
|
>>> list(our_deque_2) == list(deque_collections_2)
|
|
True
|
|
"""
|
|
node = self._Node(val, None, None)
|
|
if self.is_empty():
|
|
# front = back
|
|
self._front = self._back = node
|
|
self._len = 1
|
|
else:
|
|
# connect nodes
|
|
self._back.next_node = node
|
|
node.prev_node = self._back
|
|
self._back = node # assign new back to the new node
|
|
|
|
self._len += 1
|
|
|
|
# make sure there were no errors
|
|
assert not self.is_empty(), "Error on appending value."
|
|
|
|
def appendleft(self, val: Any) -> None:
|
|
"""
|
|
Adds val to the beginning of the deque.
|
|
Time complexity: O(1)
|
|
>>> our_deque_1 = Deque([2, 3])
|
|
>>> our_deque_1.appendleft(1)
|
|
>>> our_deque_1
|
|
[1, 2, 3]
|
|
>>> our_deque_2 = Deque('bc')
|
|
>>> our_deque_2.appendleft('a')
|
|
>>> our_deque_2
|
|
['a', 'b', 'c']
|
|
>>> from collections import deque
|
|
>>> deque_collections_1 = deque([2, 3])
|
|
>>> deque_collections_1.appendleft(1)
|
|
>>> deque_collections_1
|
|
deque([1, 2, 3])
|
|
>>> deque_collections_2 = deque('bc')
|
|
>>> deque_collections_2.appendleft('a')
|
|
>>> deque_collections_2
|
|
deque(['a', 'b', 'c'])
|
|
>>> list(our_deque_1) == list(deque_collections_1)
|
|
True
|
|
>>> list(our_deque_2) == list(deque_collections_2)
|
|
True
|
|
"""
|
|
node = self._Node(val, None, None)
|
|
if self.is_empty():
|
|
# front = back
|
|
self._front = self._back = node
|
|
self._len = 1
|
|
else:
|
|
# connect nodes
|
|
node.next_node = self._front
|
|
self._front.prev_node = node
|
|
self._front = node # assign new front to the new node
|
|
|
|
self._len += 1
|
|
|
|
# make sure there were no errors
|
|
assert not self.is_empty(), "Error on appending value."
|
|
|
|
def extend(self, iterable: Iterable[Any]) -> None:
|
|
"""
|
|
Appends every value of iterable to the end of the deque.
|
|
Time complexity: O(n)
|
|
>>> our_deque_1 = Deque([1, 2, 3])
|
|
>>> our_deque_1.extend([4, 5])
|
|
>>> our_deque_1
|
|
[1, 2, 3, 4, 5]
|
|
>>> our_deque_2 = Deque('ab')
|
|
>>> our_deque_2.extend('cd')
|
|
>>> our_deque_2
|
|
['a', 'b', 'c', 'd']
|
|
>>> from collections import deque
|
|
>>> deque_collections_1 = deque([1, 2, 3])
|
|
>>> deque_collections_1.extend([4, 5])
|
|
>>> deque_collections_1
|
|
deque([1, 2, 3, 4, 5])
|
|
>>> deque_collections_2 = deque('ab')
|
|
>>> deque_collections_2.extend('cd')
|
|
>>> deque_collections_2
|
|
deque(['a', 'b', 'c', 'd'])
|
|
>>> list(our_deque_1) == list(deque_collections_1)
|
|
True
|
|
>>> list(our_deque_2) == list(deque_collections_2)
|
|
True
|
|
"""
|
|
for val in iterable:
|
|
self.append(val)
|
|
|
|
def extendleft(self, iterable: Iterable[Any]) -> None:
|
|
"""
|
|
Appends every value of iterable to the beginning of the deque.
|
|
Time complexity: O(n)
|
|
>>> our_deque_1 = Deque([1, 2, 3])
|
|
>>> our_deque_1.extendleft([0, -1])
|
|
>>> our_deque_1
|
|
[-1, 0, 1, 2, 3]
|
|
>>> our_deque_2 = Deque('cd')
|
|
>>> our_deque_2.extendleft('ba')
|
|
>>> our_deque_2
|
|
['a', 'b', 'c', 'd']
|
|
>>> from collections import deque
|
|
>>> deque_collections_1 = deque([1, 2, 3])
|
|
>>> deque_collections_1.extendleft([0, -1])
|
|
>>> deque_collections_1
|
|
deque([-1, 0, 1, 2, 3])
|
|
>>> deque_collections_2 = deque('cd')
|
|
>>> deque_collections_2.extendleft('ba')
|
|
>>> deque_collections_2
|
|
deque(['a', 'b', 'c', 'd'])
|
|
>>> list(our_deque_1) == list(deque_collections_1)
|
|
True
|
|
>>> list(our_deque_2) == list(deque_collections_2)
|
|
True
|
|
"""
|
|
for val in iterable:
|
|
self.appendleft(val)
|
|
|
|
def pop(self) -> Any:
|
|
"""
|
|
Removes the last element of the deque and returns it.
|
|
Time complexity: O(1)
|
|
@returns topop.val: the value of the node to pop.
|
|
>>> our_deque1 = Deque([1])
|
|
>>> our_popped1 = our_deque1.pop()
|
|
>>> our_popped1
|
|
1
|
|
>>> our_deque1
|
|
[]
|
|
|
|
>>> our_deque2 = Deque([1, 2, 3, 15182])
|
|
>>> our_popped2 = our_deque2.pop()
|
|
>>> our_popped2
|
|
15182
|
|
>>> our_deque2
|
|
[1, 2, 3]
|
|
|
|
>>> from collections import deque
|
|
>>> deque_collections = deque([1, 2, 3, 15182])
|
|
>>> collections_popped = deque_collections.pop()
|
|
>>> collections_popped
|
|
15182
|
|
>>> deque_collections
|
|
deque([1, 2, 3])
|
|
>>> list(our_deque2) == list(deque_collections)
|
|
True
|
|
>>> our_popped2 == collections_popped
|
|
True
|
|
"""
|
|
# make sure the deque has elements to pop
|
|
assert not self.is_empty(), "Deque is empty."
|
|
|
|
topop = self._back
|
|
# if only one element in the queue: point the front and back to None
|
|
# else remove one element from back
|
|
if self._front == self._back:
|
|
self._front = None
|
|
self._back = None
|
|
else:
|
|
self._back = self._back.prev_node # set new back
|
|
# drop the last node, python will deallocate memory automatically
|
|
self._back.next_node = None
|
|
|
|
self._len -= 1
|
|
|
|
return topop.val
|
|
|
|
def popleft(self) -> Any:
|
|
"""
|
|
Removes the first element of the deque and returns it.
|
|
Time complexity: O(1)
|
|
@returns topop.val: the value of the node to pop.
|
|
>>> our_deque1 = Deque([1])
|
|
>>> our_popped1 = our_deque1.pop()
|
|
>>> our_popped1
|
|
1
|
|
>>> our_deque1
|
|
[]
|
|
>>> our_deque2 = Deque([15182, 1, 2, 3])
|
|
>>> our_popped2 = our_deque2.popleft()
|
|
>>> our_popped2
|
|
15182
|
|
>>> our_deque2
|
|
[1, 2, 3]
|
|
>>> from collections import deque
|
|
>>> deque_collections = deque([15182, 1, 2, 3])
|
|
>>> collections_popped = deque_collections.popleft()
|
|
>>> collections_popped
|
|
15182
|
|
>>> deque_collections
|
|
deque([1, 2, 3])
|
|
>>> list(our_deque2) == list(deque_collections)
|
|
True
|
|
>>> our_popped2 == collections_popped
|
|
True
|
|
"""
|
|
# make sure the deque has elements to pop
|
|
assert not self.is_empty(), "Deque is empty."
|
|
|
|
topop = self._front
|
|
# if only one element in the queue: point the front and back to None
|
|
# else remove one element from front
|
|
if self._front == self._back:
|
|
self._front = None
|
|
self._back = None
|
|
else:
|
|
self._front = self._front.next_node # set new front and drop the first node
|
|
self._front.prev_node = None
|
|
|
|
self._len -= 1
|
|
|
|
return topop.val
|
|
|
|
def is_empty(self) -> bool:
|
|
"""
|
|
Checks if the deque is empty.
|
|
Time complexity: O(1)
|
|
>>> our_deque = Deque([1, 2, 3])
|
|
>>> our_deque.is_empty()
|
|
False
|
|
>>> our_empty_deque = Deque()
|
|
>>> our_empty_deque.is_empty()
|
|
True
|
|
>>> from collections import deque
|
|
>>> empty_deque_collections = deque()
|
|
>>> list(our_empty_deque) == list(empty_deque_collections)
|
|
True
|
|
"""
|
|
return self._front is None
|
|
|
|
def __len__(self) -> int:
|
|
"""
|
|
Implements len() function. Returns the length of the deque.
|
|
Time complexity: O(1)
|
|
>>> our_deque = Deque([1, 2, 3])
|
|
>>> len(our_deque)
|
|
3
|
|
>>> our_empty_deque = Deque()
|
|
>>> len(our_empty_deque)
|
|
0
|
|
>>> from collections import deque
|
|
>>> deque_collections = deque([1, 2, 3])
|
|
>>> len(deque_collections)
|
|
3
|
|
>>> empty_deque_collections = deque()
|
|
>>> len(empty_deque_collections)
|
|
0
|
|
>>> len(our_empty_deque) == len(empty_deque_collections)
|
|
True
|
|
"""
|
|
return self._len
|
|
|
|
def __eq__(self, other: object) -> bool:
|
|
"""
|
|
Implements "==" operator. Returns if *self* is equal to *other*.
|
|
Time complexity: O(n)
|
|
>>> our_deque_1 = Deque([1, 2, 3])
|
|
>>> our_deque_2 = Deque([1, 2, 3])
|
|
>>> our_deque_1 == our_deque_2
|
|
True
|
|
>>> our_deque_3 = Deque([1, 2])
|
|
>>> our_deque_1 == our_deque_3
|
|
False
|
|
>>> from collections import deque
|
|
>>> deque_collections_1 = deque([1, 2, 3])
|
|
>>> deque_collections_2 = deque([1, 2, 3])
|
|
>>> deque_collections_1 == deque_collections_2
|
|
True
|
|
>>> deque_collections_3 = deque([1, 2])
|
|
>>> deque_collections_1 == deque_collections_3
|
|
False
|
|
>>> (our_deque_1 == our_deque_2) == (deque_collections_1 == deque_collections_2)
|
|
True
|
|
>>> (our_deque_1 == our_deque_3) == (deque_collections_1 == deque_collections_3)
|
|
True
|
|
"""
|
|
|
|
if not isinstance(other, Deque):
|
|
return NotImplemented
|
|
|
|
me = self._front
|
|
oth = other._front
|
|
|
|
# if the length of the dequeues are not the same, they are not equal
|
|
if len(self) != len(other):
|
|
return False
|
|
|
|
while me is not None and oth is not None:
|
|
# compare every value
|
|
if me.val != oth.val:
|
|
return False
|
|
me = me.next_node
|
|
oth = oth.next_node
|
|
|
|
return True
|
|
|
|
def __iter__(self) -> Deque._Iterator:
|
|
"""
|
|
Implements iteration.
|
|
Time complexity: O(1)
|
|
>>> our_deque = Deque([1, 2, 3])
|
|
>>> for v in our_deque:
|
|
... print(v)
|
|
1
|
|
2
|
|
3
|
|
>>> from collections import deque
|
|
>>> deque_collections = deque([1, 2, 3])
|
|
>>> for v in deque_collections:
|
|
... print(v)
|
|
1
|
|
2
|
|
3
|
|
"""
|
|
return Deque._Iterator(self._front)
|
|
|
|
def __repr__(self) -> str:
|
|
"""
|
|
Implements representation of the deque.
|
|
Represents it as a list, with its values between '[' and ']'.
|
|
Time complexity: O(n)
|
|
>>> our_deque = Deque([1, 2, 3])
|
|
>>> our_deque
|
|
[1, 2, 3]
|
|
"""
|
|
values_list = []
|
|
aux = self._front
|
|
while aux is not None:
|
|
# append the values in a list to display
|
|
values_list.append(aux.val)
|
|
aux = aux.next_node
|
|
|
|
return f"[{', '.join(repr(val) for val in values_list)}]"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import doctest
|
|
|
|
doctest.testmod()
|
|
dq = Deque([3])
|
|
dq.pop()
|