From 0104de1c294548479cf2e55f07726b18c92c178c Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 18 Oct 2021 23:52:59 -0300 Subject: [PATCH 1/9] Add deque_from_scratch.py --- data_structures/queue/deque_from_scratch.py | 279 ++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 data_structures/queue/deque_from_scratch.py diff --git a/data_structures/queue/deque_from_scratch.py b/data_structures/queue/deque_from_scratch.py new file mode 100644 index 000000000000..66a74359e259 --- /dev/null +++ b/data_structures/queue/deque_from_scratch.py @@ -0,0 +1,279 @@ +""" +Implementation of double ended queue. +""" +from dataclasses import dataclass +from typing import Any + +class Deque: + """ + Deque data structure. + + Operations + ---------- + append(val: Any) -> None + appendleft(val: Any) -> 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" = None + prev: "_Node" = 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): + self._cur = cur + + def __iter__(self): + return self + + def __next__(self): + if self._cur is None: + # finished iterating + raise StopIteration + val = self._cur.val + self._cur = self._cur.next + + return val + + def __init__(self, iterable = None): + self._front = self._back = None + self._len = 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) + + >>> d = Deque([1, 2, 3]) + >>> d.append(4) + >>> print(d) + [1, 2, 3, 4] + >>> d2 = Deque([51, "str", True]) + >>> d2.append([1, "2"]) + >>> print(d2) + [51, 'str', True, [1, '2']] + """ + 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.prev = self._back + self._back = node # assign new back to the new node + + self._len += 1 + + # make sure there was 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) + + >>> d = Deque([2, 3]) + >>> d.appendleft(1) + >>> print(d) + [1, 2, 3] + >>> d2 = Deque(["b", "c"]) + >>> d2.appendleft("a") + >>> print(d2) + ['a', 'b', 'c'] + """ + 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 = self._front + self._front.prev = node + self._front = node # assign new front to the new node + + self._len += 1 + + # make sure there was no errors + assert not self.is_empty(), "Error on appending value." + + 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. + + >>> d = Deque([1, 2, 3, 15182]) + >>> popped = d.pop() + >>> print(popped) + 15182 + >>> print(d) + [1, 2, 3] + """ + # make sure the deque has elements to pop + assert not self.is_empty(), "Deque is empty." + + topop = self._back + self._back = self._back.prev # set new back + self._back.next = None # drop the last node - python will deallocate memory automatically + + 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. + + >>> d = Deque([15182, 1, 2, 3]) + >>> popped = d.popleft() + >>> print(popped) + 15182 + >>> print(d) + [1, 2, 3] + """ + # make sure the deque has elements to pop + assert not self.is_empty(), "Deque is empty." + + topop = self._front + self._front = self._front.next # set new front and drop the first node + self._front.prev = None + + self._len -= 1 + + return topop.val + + def is_empty(self) -> bool: + """ + Checks if the deque is empty. + Time complexity: O(1) + + >>> d = Deque([1, 2, 3]) + >>> print(d.is_empty()) + False + >>> d2 = Deque() + >>> print(d2.is_empty()) + True + """ + return self._front is None + + def __len__(self) -> int: + """ + Implements len() function. Returns the length of the deque. + Time complexity: O(1) + + >>> d = Deque([1, 2, 3]) + >>> print(len(d)) + 3 + >>> d2 = Deque() + >>> print(len(d2)) + 0 + """ + return self._len + + def __eq__(self, other) -> bool: + """ + Implements "==" operator. Returns if *self* is equal to *other*. + Time complexity: O(n) + + >>> d = Deque([1, 2, 3]) + >>> d2 = Deque([1, 2, 3]) + >>> print(d == d2) + True + >>> d3 = Deque([1, 2]) + >>> print(d == d3) + False + """ + me = self._front + oth = other._front + + # if the length of the deques 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 + oth = oth.next + + return True + + def __iter__(self): + """ + Implements iteration. + Time complexity: O(1) + + >>> d = Deque([1, 2, 3]) + >>> for v in d: + ... print(v) + 1 + 2 + 3 + """ + return Deque._Iterator(self._front) + + def __repr__(self) -> str: + """ + Implements representation of the deque. + Time complexity: O(n) + + >>> d = Deque([1, 2, 3]) + >>> print(d) + [1, 2, 3] + """ + l = [] + aux = self._front + while aux is not None: + # append the values in a list to display + l.append(aux.val) + aux = aux.next + + return '[' + ', '.join(repr(x) for x in l) + ']' \ No newline at end of file From b105bebe7c1c2fea810fbf3d42d0d40b7e81b6af Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 19 Oct 2021 00:19:33 -0300 Subject: [PATCH 2/9] added deque_from_scratch.py --- data_structures/queue/deque_from_scratch.py | 29 ++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/data_structures/queue/deque_from_scratch.py b/data_structures/queue/deque_from_scratch.py index 66a74359e259..0cad88f50363 100644 --- a/data_structures/queue/deque_from_scratch.py +++ b/data_structures/queue/deque_from_scratch.py @@ -11,8 +11,11 @@ class Deque: Operations ---------- append(val: Any) -> None + appendleft(val: Any) -> None + pop() -> Any + popleft() -> Any @@ -55,13 +58,27 @@ class _Iterator: """ __slots__ = ["_cur"] - def __init__(self, cur): + def __init__(self, cur: "_Node") -> None: self._cur = cur - def __iter__(self): + def __iter__(self) -> "_Iterator": + """ + >>> d = Deque([1, 2, 3]) + >>> iterator = iter(d) + """ return self - def __next__(self): + def __next__(self) -> Any: + """ + >>> d = Deque([1, 2, 3]) + >>> iterator = iter(d) + >>> next(iterator) + 1 + >>> next(iterator) + 2 + >>> next(iterator) + 3 + """ if self._cur is None: # finished iterating raise StopIteration @@ -70,7 +87,7 @@ def __next__(self): return val - def __init__(self, iterable = None): + def __init__(self, iterable: list = None) -> None: self._front = self._back = None self._len = 0 @@ -217,7 +234,7 @@ def __len__(self) -> int: """ return self._len - def __eq__(self, other) -> bool: + def __eq__(self, other: "Deque") -> bool: """ Implements "==" operator. Returns if *self* is equal to *other*. Time complexity: O(n) @@ -246,7 +263,7 @@ def __eq__(self, other) -> bool: return True - def __iter__(self): + def __iter__(self) -> "_Iterator": """ Implements iteration. Time complexity: O(1) From 43299f233bba79d0d8fd06ad2da3b2af5805663d Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 19 Oct 2021 07:54:53 -0300 Subject: [PATCH 3/9] add extend, extendleft and make comparison --- data_structures/queue/deque_from_scratch.py | 89 +++++++++++++++++++-- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/data_structures/queue/deque_from_scratch.py b/data_structures/queue/deque_from_scratch.py index 0cad88f50363..b04979743ec6 100644 --- a/data_structures/queue/deque_from_scratch.py +++ b/data_structures/queue/deque_from_scratch.py @@ -3,6 +3,7 @@ """ from dataclasses import dataclass from typing import Any +import collections # just for doctests class Deque: """ @@ -105,10 +106,11 @@ def append(self, val: Any) -> None: >>> d.append(4) >>> print(d) [1, 2, 3, 4] - >>> d2 = Deque([51, "str", True]) - >>> d2.append([1, "2"]) + + >>> d2 = collections.deque([1, 2, 3]) + >>> d2.append(4) >>> print(d2) - [51, 'str', True, [1, '2']] + deque([1, 2, 3, 4]) """ node = self._Node(val, None, None) if self.is_empty(): @@ -135,10 +137,11 @@ def appendleft(self, val: Any) -> None: >>> d.appendleft(1) >>> print(d) [1, 2, 3] - >>> d2 = Deque(["b", "c"]) - >>> d2.appendleft("a") + + >>> d2 = collections.deque([2, 3]) + >>> d2.appendleft(1) >>> print(d2) - ['a', 'b', 'c'] + deque([1, 2, 3]) """ node = self._Node(val, None, None) if self.is_empty(): @@ -156,6 +159,40 @@ def appendleft(self, val: Any) -> None: # make sure there was no errors assert not self.is_empty(), "Error on appending value." + def extend(self, iter: list) -> None: + """ + Appends every value of iter to the end of the deque. + + >>> d = Deque([1, 2, 3]) + >>> d.extend([4, 5]) + >>> print(d) + [1, 2, 3, 4, 5] + + >>> d2 = collections.deque([1, 2, 3]) + >>> d2.extend([4, 5]) + >>> print(d2) + deque([1, 2, 3, 4, 5]) + """ + for val in iter: + self.append(val) + + def extendleft(self, iter: list) -> None: + """ + Appends every value of iter to the beginning of the deque. + + >>> d = Deque([1, 2, 3]) + >>> d.extendleft([0, -1]) + >>> print(d) + [-1, 0, 1, 2, 3] + + >>> d2 = collections.deque([1, 2, 3]) + >>> d2.extendleft([0, -1]) + >>> print(d2) + deque([-1, 0, 1, 2, 3]) + """ + for val in iter: + self.appendleft(val) + def pop(self) -> Any: """ Removes the last element of the deque and returns it. @@ -169,6 +206,13 @@ def pop(self) -> Any: 15182 >>> print(d) [1, 2, 3] + + >>> d2 = collections.deque([1, 2, 3, 15182]) + >>> popped = d2.pop() + >>> print(popped) + 15182 + >>> print(d2) + deque([1, 2, 3]) """ # make sure the deque has elements to pop assert not self.is_empty(), "Deque is empty." @@ -194,6 +238,13 @@ def popleft(self) -> Any: 15182 >>> print(d) [1, 2, 3] + + >>> d2 = collections.deque([15182, 1, 2, 3]) + >>> popped = d2.popleft() + >>> print(popped) + 15182 + >>> print(d2) + deque([1, 2, 3]) """ # make sure the deque has elements to pop assert not self.is_empty(), "Deque is empty." @@ -231,6 +282,13 @@ def __len__(self) -> int: >>> d2 = Deque() >>> print(len(d2)) 0 + + >>> d3 = collections.deque([1, 2, 3]) + >>> print(len(d3)) + 3 + >>> d4 = collections.deque() + >>> print(len(d4)) + 0 """ return self._len @@ -246,6 +304,14 @@ def __eq__(self, other: "Deque") -> bool: >>> d3 = Deque([1, 2]) >>> print(d == d3) False + + >>> d4 = collections.deque([1, 2, 3]) + >>> d5 = collections.deque([1, 2, 3]) + >>> print(d4 == d5) + True + >>> d6 = collections.deque([1, 2]) + >>> print(d5 == d6) + False """ me = self._front oth = other._front @@ -274,12 +340,19 @@ def __iter__(self) -> "_Iterator": 1 2 3 + + >>> d2 = collections.deque([1, 2, 3]) + >>> for v in d: + ... print(v) + 1 + 2 + 3 """ return Deque._Iterator(self._front) def __repr__(self) -> str: """ - Implements representation of the deque. + Implements representation of the deque. Represents it as a list, with its values between '[' and ']'. Time complexity: O(n) >>> d = Deque([1, 2, 3]) @@ -293,4 +366,4 @@ def __repr__(self) -> str: l.append(aux.val) aux = aux.next - return '[' + ', '.join(repr(x) for x in l) + ']' \ No newline at end of file + return '[' + ', '.join(repr(x) for x in l) + ']' From 2f9ff234142337763f59b82e33251104804543da Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 19 Oct 2021 08:07:31 -0300 Subject: [PATCH 4/9] updated operations list --- data_structures/queue/deque_from_scratch.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data_structures/queue/deque_from_scratch.py b/data_structures/queue/deque_from_scratch.py index b04979743ec6..ec79a93f5560 100644 --- a/data_structures/queue/deque_from_scratch.py +++ b/data_structures/queue/deque_from_scratch.py @@ -15,6 +15,10 @@ class Deque: appendleft(val: Any) -> None + extend(iter: list) -> None + + extendleft(iter: list) -> None + pop() -> Any popleft() -> Any From f1c203fe7a2c9b98f58f5b620708b6b98468a128 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 19 Oct 2021 09:10:48 -0300 Subject: [PATCH 5/9] fix doctest on Deque.__iter__ --- data_structures/queue/deque_from_scratch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/queue/deque_from_scratch.py b/data_structures/queue/deque_from_scratch.py index ec79a93f5560..3685b088ad4a 100644 --- a/data_structures/queue/deque_from_scratch.py +++ b/data_structures/queue/deque_from_scratch.py @@ -346,7 +346,7 @@ def __iter__(self) -> "_Iterator": 3 >>> d2 = collections.deque([1, 2, 3]) - >>> for v in d: + >>> for v in d2: ... print(v) 1 2 From 2a31cc9cc0bdde3fff547c7b8502045db09192e0 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 19 Oct 2021 09:21:58 -0300 Subject: [PATCH 6/9] pre-commit fix --- data_structures/queue/deque_from_scratch.py | 26 +++++++++++++-------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/data_structures/queue/deque_from_scratch.py b/data_structures/queue/deque_from_scratch.py index 3685b088ad4a..55b2130f1246 100644 --- a/data_structures/queue/deque_from_scratch.py +++ b/data_structures/queue/deque_from_scratch.py @@ -1,9 +1,10 @@ """ Implementation of double ended queue. """ +import collections # just for doctests from dataclasses import dataclass from typing import Any -import collections # just for doctests + class Deque: """ @@ -40,14 +41,16 @@ class Deque: _len: int the number of nodes """ + __slots__ = ["_front", "_back", "_len"] @dataclass class _Node: """ - Representation of a 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" = None prev: "_Node" = None @@ -61,6 +64,7 @@ class _Iterator: _cur: _Node the current node of the iteration. """ + __slots__ = ["_cur"] def __init__(self, cur: "_Node") -> None: @@ -125,7 +129,7 @@ def append(self, val: Any) -> None: # connect nodes self._back.next = node node.prev = self._back - self._back = node # assign new back to the new node + self._back = node # assign new back to the new node self._len += 1 @@ -156,7 +160,7 @@ def appendleft(self, val: Any) -> None: # connect nodes node.next = self._front self._front.prev = node - self._front = node # assign new front to the new node + self._front = node # assign new front to the new node self._len += 1 @@ -166,7 +170,7 @@ def appendleft(self, val: Any) -> None: def extend(self, iter: list) -> None: """ Appends every value of iter to the end of the deque. - + >>> d = Deque([1, 2, 3]) >>> d.extend([4, 5]) >>> print(d) @@ -183,7 +187,7 @@ def extend(self, iter: list) -> None: def extendleft(self, iter: list) -> None: """ Appends every value of iter to the beginning of the deque. - + >>> d = Deque([1, 2, 3]) >>> d.extendleft([0, -1]) >>> print(d) @@ -222,8 +226,10 @@ def pop(self) -> Any: assert not self.is_empty(), "Deque is empty." topop = self._back - self._back = self._back.prev # set new back - self._back.next = None # drop the last node - python will deallocate memory automatically + self._back = self._back.prev # set new back + self._back.next = ( + None # drop the last node - python will deallocate memory automatically + ) self._len -= 1 @@ -254,7 +260,7 @@ def popleft(self) -> Any: assert not self.is_empty(), "Deque is empty." topop = self._front - self._front = self._front.next # set new front and drop the first node + self._front = self._front.next # set new front and drop the first node self._front.prev = None self._len -= 1 @@ -370,4 +376,4 @@ def __repr__(self) -> str: l.append(aux.val) aux = aux.next - return '[' + ', '.join(repr(x) for x in l) + ']' + return "[" + ", ".join(repr(x) for x in l) + "]" From 4a493e4daa6c630a30e8a7d5c50e9f42ac94afa0 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 19 Oct 2021 09:30:57 -0300 Subject: [PATCH 7/9] time complexity comments, change type hints --- data_structures/queue/deque_from_scratch.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/data_structures/queue/deque_from_scratch.py b/data_structures/queue/deque_from_scratch.py index 55b2130f1246..c5485b23c312 100644 --- a/data_structures/queue/deque_from_scratch.py +++ b/data_structures/queue/deque_from_scratch.py @@ -3,7 +3,7 @@ """ import collections # just for doctests from dataclasses import dataclass -from typing import Any +from typing import Any, Iterable class Deque: @@ -96,7 +96,7 @@ def __next__(self) -> Any: return val - def __init__(self, iterable: list = None) -> None: + def __init__(self, iterable: Iterable = None) -> None: self._front = self._back = None self._len = 0 @@ -167,9 +167,10 @@ def appendleft(self, val: Any) -> None: # make sure there was no errors assert not self.is_empty(), "Error on appending value." - def extend(self, iter: list) -> None: + def extend(self, iter: Iterable) -> None: """ Appends every value of iter to the end of the deque. + Time complexity: O(n) >>> d = Deque([1, 2, 3]) >>> d.extend([4, 5]) @@ -184,9 +185,10 @@ def extend(self, iter: list) -> None: for val in iter: self.append(val) - def extendleft(self, iter: list) -> None: + def extendleft(self, iter: Iterable) -> None: """ Appends every value of iter to the beginning of the deque. + Time complexity: O(n) >>> d = Deque([1, 2, 3]) >>> d.extendleft([0, -1]) From 9f6c76605ed89870164157b39090ade18ab25461 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 19 Oct 2021 11:47:52 -0300 Subject: [PATCH 8/9] pre-commit fix --- data_structures/queue/deque_from_scratch.py | 31 +++++++++++++-------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/data_structures/queue/deque_from_scratch.py b/data_structures/queue/deque_from_scratch.py index c5485b23c312..657dc2f00b0a 100644 --- a/data_structures/queue/deque_from_scratch.py +++ b/data_structures/queue/deque_from_scratch.py @@ -1,7 +1,6 @@ """ Implementation of double ended queue. """ -import collections # just for doctests from dataclasses import dataclass from typing import Any, Iterable @@ -16,9 +15,9 @@ class Deque: appendleft(val: Any) -> None - extend(iter: list) -> None + extend(iter: Iterable) -> None - extendleft(iter: list) -> None + extendleft(iter: Iterable) -> None pop() -> Any @@ -52,8 +51,8 @@ class _Node: """ val: Any = None - next: "_Node" = None - prev: "_Node" = None + next: "Deque._Node" = None + prev: "Deque._Node" = None class _Iterator: """ @@ -67,10 +66,10 @@ class _Iterator: __slots__ = ["_cur"] - def __init__(self, cur: "_Node") -> None: + def __init__(self, cur: "Deque._Node") -> None: self._cur = cur - def __iter__(self) -> "_Iterator": + def __iter__(self) -> "Deque._Iterator": """ >>> d = Deque([1, 2, 3]) >>> iterator = iter(d) @@ -115,6 +114,7 @@ def append(self, val: Any) -> None: >>> print(d) [1, 2, 3, 4] + >>> import collections >>> d2 = collections.deque([1, 2, 3]) >>> d2.append(4) >>> print(d2) @@ -146,6 +146,7 @@ def appendleft(self, val: Any) -> None: >>> print(d) [1, 2, 3] + >>> import collections >>> d2 = collections.deque([2, 3]) >>> d2.appendleft(1) >>> print(d2) @@ -177,6 +178,7 @@ def extend(self, iter: Iterable) -> None: >>> print(d) [1, 2, 3, 4, 5] + >>> import collections >>> d2 = collections.deque([1, 2, 3]) >>> d2.extend([4, 5]) >>> print(d2) @@ -195,6 +197,7 @@ def extendleft(self, iter: Iterable) -> None: >>> print(d) [-1, 0, 1, 2, 3] + >>> import collections >>> d2 = collections.deque([1, 2, 3]) >>> d2.extendleft([0, -1]) >>> print(d2) @@ -217,6 +220,7 @@ def pop(self) -> Any: >>> print(d) [1, 2, 3] + >>> import collections >>> d2 = collections.deque([1, 2, 3, 15182]) >>> popped = d2.pop() >>> print(popped) @@ -251,6 +255,7 @@ def popleft(self) -> Any: >>> print(d) [1, 2, 3] + >>> import collections >>> d2 = collections.deque([15182, 1, 2, 3]) >>> popped = d2.popleft() >>> print(popped) @@ -295,6 +300,7 @@ def __len__(self) -> int: >>> print(len(d2)) 0 + >>> import collections >>> d3 = collections.deque([1, 2, 3]) >>> print(len(d3)) 3 @@ -317,6 +323,7 @@ def __eq__(self, other: "Deque") -> bool: >>> print(d == d3) False + >>> import collections >>> d4 = collections.deque([1, 2, 3]) >>> d5 = collections.deque([1, 2, 3]) >>> print(d4 == d5) @@ -353,6 +360,7 @@ def __iter__(self) -> "_Iterator": 2 3 + >>> import collections >>> d2 = collections.deque([1, 2, 3]) >>> for v in d2: ... print(v) @@ -364,18 +372,19 @@ def __iter__(self) -> "_Iterator": def __repr__(self) -> str: """ - Implements representation of the deque. Represents it as a list, with its values between '[' and ']'. + Implements representation of the deque. + Represents it as a list, with its values between '[' and ']'. Time complexity: O(n) >>> d = Deque([1, 2, 3]) >>> print(d) [1, 2, 3] """ - l = [] + values_list = [] aux = self._front while aux is not None: # append the values in a list to display - l.append(aux.val) + values_list.append(aux.val) aux = aux.next - return "[" + ", ".join(repr(x) for x in l) + "]" + return "[" + ", ".join(repr(val) for val in values_list) + "]" From 11a54cb729f99d57ae4e87de3c8f0812cb02657e Mon Sep 17 00:00:00 2001 From: Benjamin Date: Wed, 20 Oct 2021 11:01:05 -0300 Subject: [PATCH 9/9] added more doctests --- data_structures/queue/deque_from_scratch.py | 390 --------------- data_structures/queue/double_ended_queue.py | 495 ++++++++++++++++++-- 2 files changed, 454 insertions(+), 431 deletions(-) delete mode 100644 data_structures/queue/deque_from_scratch.py diff --git a/data_structures/queue/deque_from_scratch.py b/data_structures/queue/deque_from_scratch.py deleted file mode 100644 index 657dc2f00b0a..000000000000 --- a/data_structures/queue/deque_from_scratch.py +++ /dev/null @@ -1,390 +0,0 @@ -""" -Implementation of double ended queue. -""" -from dataclasses import dataclass -from typing import Any, Iterable - - -class Deque: - """ - Deque data structure. - - Operations - ---------- - append(val: Any) -> None - - appendleft(val: Any) -> None - - extend(iter: Iterable) -> None - - extendleft(iter: 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: "Deque._Node" = None - prev: "Deque._Node" = 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: - self._cur = cur - - def __iter__(self) -> "Deque._Iterator": - """ - >>> d = Deque([1, 2, 3]) - >>> iterator = iter(d) - """ - return self - - def __next__(self) -> Any: - """ - >>> d = Deque([1, 2, 3]) - >>> iterator = iter(d) - >>> 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 - - return val - - def __init__(self, iterable: Iterable = None) -> None: - self._front = self._back = None - self._len = 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) - - >>> d = Deque([1, 2, 3]) - >>> d.append(4) - >>> print(d) - [1, 2, 3, 4] - - >>> import collections - >>> d2 = collections.deque([1, 2, 3]) - >>> d2.append(4) - >>> print(d2) - deque([1, 2, 3, 4]) - """ - 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.prev = self._back - self._back = node # assign new back to the new node - - self._len += 1 - - # make sure there was 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) - - >>> d = Deque([2, 3]) - >>> d.appendleft(1) - >>> print(d) - [1, 2, 3] - - >>> import collections - >>> d2 = collections.deque([2, 3]) - >>> d2.appendleft(1) - >>> print(d2) - deque([1, 2, 3]) - """ - 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 = self._front - self._front.prev = node - self._front = node # assign new front to the new node - - self._len += 1 - - # make sure there was no errors - assert not self.is_empty(), "Error on appending value." - - def extend(self, iter: Iterable) -> None: - """ - Appends every value of iter to the end of the deque. - Time complexity: O(n) - - >>> d = Deque([1, 2, 3]) - >>> d.extend([4, 5]) - >>> print(d) - [1, 2, 3, 4, 5] - - >>> import collections - >>> d2 = collections.deque([1, 2, 3]) - >>> d2.extend([4, 5]) - >>> print(d2) - deque([1, 2, 3, 4, 5]) - """ - for val in iter: - self.append(val) - - def extendleft(self, iter: Iterable) -> None: - """ - Appends every value of iter to the beginning of the deque. - Time complexity: O(n) - - >>> d = Deque([1, 2, 3]) - >>> d.extendleft([0, -1]) - >>> print(d) - [-1, 0, 1, 2, 3] - - >>> import collections - >>> d2 = collections.deque([1, 2, 3]) - >>> d2.extendleft([0, -1]) - >>> print(d2) - deque([-1, 0, 1, 2, 3]) - """ - for val in iter: - 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. - - >>> d = Deque([1, 2, 3, 15182]) - >>> popped = d.pop() - >>> print(popped) - 15182 - >>> print(d) - [1, 2, 3] - - >>> import collections - >>> d2 = collections.deque([1, 2, 3, 15182]) - >>> popped = d2.pop() - >>> print(popped) - 15182 - >>> print(d2) - deque([1, 2, 3]) - """ - # make sure the deque has elements to pop - assert not self.is_empty(), "Deque is empty." - - topop = self._back - self._back = self._back.prev # set new back - self._back.next = ( - None # drop the last node - python will deallocate memory automatically - ) - - 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. - - >>> d = Deque([15182, 1, 2, 3]) - >>> popped = d.popleft() - >>> print(popped) - 15182 - >>> print(d) - [1, 2, 3] - - >>> import collections - >>> d2 = collections.deque([15182, 1, 2, 3]) - >>> popped = d2.popleft() - >>> print(popped) - 15182 - >>> print(d2) - deque([1, 2, 3]) - """ - # make sure the deque has elements to pop - assert not self.is_empty(), "Deque is empty." - - topop = self._front - self._front = self._front.next # set new front and drop the first node - self._front.prev = None - - self._len -= 1 - - return topop.val - - def is_empty(self) -> bool: - """ - Checks if the deque is empty. - Time complexity: O(1) - - >>> d = Deque([1, 2, 3]) - >>> print(d.is_empty()) - False - >>> d2 = Deque() - >>> print(d2.is_empty()) - True - """ - return self._front is None - - def __len__(self) -> int: - """ - Implements len() function. Returns the length of the deque. - Time complexity: O(1) - - >>> d = Deque([1, 2, 3]) - >>> print(len(d)) - 3 - >>> d2 = Deque() - >>> print(len(d2)) - 0 - - >>> import collections - >>> d3 = collections.deque([1, 2, 3]) - >>> print(len(d3)) - 3 - >>> d4 = collections.deque() - >>> print(len(d4)) - 0 - """ - return self._len - - def __eq__(self, other: "Deque") -> bool: - """ - Implements "==" operator. Returns if *self* is equal to *other*. - Time complexity: O(n) - - >>> d = Deque([1, 2, 3]) - >>> d2 = Deque([1, 2, 3]) - >>> print(d == d2) - True - >>> d3 = Deque([1, 2]) - >>> print(d == d3) - False - - >>> import collections - >>> d4 = collections.deque([1, 2, 3]) - >>> d5 = collections.deque([1, 2, 3]) - >>> print(d4 == d5) - True - >>> d6 = collections.deque([1, 2]) - >>> print(d5 == d6) - False - """ - me = self._front - oth = other._front - - # if the length of the deques 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 - oth = oth.next - - return True - - def __iter__(self) -> "_Iterator": - """ - Implements iteration. - Time complexity: O(1) - - >>> d = Deque([1, 2, 3]) - >>> for v in d: - ... print(v) - 1 - 2 - 3 - - >>> import collections - >>> d2 = collections.deque([1, 2, 3]) - >>> for v in d2: - ... 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) - - >>> d = Deque([1, 2, 3]) - >>> print(d) - [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 - - return "[" + ", ".join(repr(val) for val in values_list) + "]" diff --git a/data_structures/queue/double_ended_queue.py b/data_structures/queue/double_ended_queue.py index dd003b7c98ac..36106d8bc0d9 100644 --- a/data_structures/queue/double_ended_queue.py +++ b/data_structures/queue/double_ended_queue.py @@ -1,57 +1,470 @@ -# Python code to demonstrate working of -# extend(), extendleft(), rotate(), reverse() +""" +Implementation of double ended queue. +""" +from dataclasses import dataclass +from typing import Any, Iterable -# importing "collections" for deque operations -import collections -# initializing deque -de = collections.deque([1, 2, 3]) +class Deque: + """ + Deque data structure. -# using extend() to add numbers to right end -# adds 4,5,6 to right end -de.extend([4, 5, 6]) + Operations + ---------- + append(val: Any) -> None -# printing modified deque -print("The deque after extending deque at end is : ") -print(de) + appendleft(val: Any) -> None -# using extendleft() to add numbers to left end -# adds 7,8,9 to right end -de.extendleft([7, 8, 9]) + extend(iter: Iterable) -> None -# printing modified deque -print("The deque after extending deque at beginning is : ") -print(de) + extendleft(iter: Iterable) -> None -# using rotate() to rotate the deque -# rotates by 3 to left -de.rotate(-3) + pop() -> Any -# printing modified deque -print("The deque after rotating deque is : ") -print(de) + popleft() -> Any -# using reverse() to reverse the deque -de.reverse() -# printing modified deque -print("The deque after reversing deque is : ") -print(de) + Observers + --------- + is_empty() -> bool -# get right-end value and eliminate -startValue = de.pop() -print("The deque after popping value at end is : ") -print(de) + Attributes + ---------- + _front: _Node + front of the deque a.k.a. the first element -# get left-end value and eliminate -endValue = de.popleft() + _back: _Node + back of the element a.k.a. the last element -print("The deque after popping value at start is : ") -print(de) + _len: int + the number of nodes + """ -# eliminate element searched by value -de.remove(5) + __slots__ = ["_front", "_back", "_len"] -print("The deque after eliminating element searched by value : ") -print(de) + @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: "Deque._Node" = None + prev: "Deque._Node" = 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: + 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 + + return val + + def __init__(self, iterable: Iterable = None) -> None: + self._front = self._back = None + self._len = 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.prev = 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 = self._front + self._front.prev = 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, iter: Iterable) -> None: + """ + Appends every value of iter 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 iter: + self.append(val) + + def extendleft(self, iter: Iterable) -> None: + """ + Appends every value of iter 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 iter: + 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_deque = Deque([1, 2, 3, 15182]) + >>> our_popped = our_deque.pop() + >>> our_popped + 15182 + >>> our_deque + [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_deque) == list(deque_collections) + True + >>> our_popped == collections_popped + True + """ + # make sure the deque has elements to pop + assert not self.is_empty(), "Deque is empty." + + topop = self._back + self._back = self._back.prev # set new back + self._back.next = ( + None # drop the last node - python will deallocate memory automatically + ) + + 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_deque = Deque([15182, 1, 2, 3]) + >>> our_popped = our_deque.popleft() + >>> our_popped + 15182 + >>> our_deque + [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_deque) == list(deque_collections) + True + >>> our_popped == collections_popped + True + """ + # make sure the deque has elements to pop + assert not self.is_empty(), "Deque is empty." + + topop = self._front + self._front = self._front.next # set new front and drop the first node + self._front.prev = 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: "Deque") -> 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 + """ + me = self._front + oth = other._front + + # if the length of the deques 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 + oth = oth.next + + return True + + def __iter__(self) -> "_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 + + return "[" + ", ".join(repr(val) for val in values_list) + "]" + + +if __name__ == "__main__": + import doctest + + doctest.testmod()