data_structures
1from .node import NodeCore, NodePriorityQueue, TreeNode 2from .linked_list import LinkedList 3from .doubly_linked_list import Node, DoublyLinkedList 4from .queues import QueueArray, LinkedQueue, CircularQueue, PriorityQueue 5from .stack import StackArray, LinkedStack 6from .avl_tree import AVLTreeNode, AVLTree 7from .red_black_tree import Color, RBTreeNode, RedBlackTree 8from .binary_search_tree import ( 9 traversal_keywords, 10 copy_keywords, 11 default_keyword, 12 BinarySearchTree, 13) 14 15__all__ = [ 16 "NodeCore", 17 "NodePriorityQueue", 18 "TreeNode", 19 "LinkedList", 20 "Node", 21 "DoublyLinkedList", 22 "QueueArray", 23 "LinkedQueue", 24 "CircularQueue", 25 "PriorityQueue", 26 "StackArray", 27 "LinkedStack", 28 "traversal_keywords", 29 "copy_keywords", 30 "default_keyword", 31 "BinarySearchTree", 32 "AVLTreeNode", 33 "AVLTree", 34 "Color", 35 "RBTreeNode", 36 "RedBlackTree", 37]
5class NodeCore: 6 """This is the fundamental class that's used & inherited for linear data structures. 7 8 Attributes: 9 value (any): Value (data) stored in node. 10 next_node (Node | None): A pointer to the next node if exists. 11 """ 12 13 def __init__(self, value, next_node=None): 14 """Initialize a NodeCore object. 15 16 Args: 17 value (Any, optional): First value to add to the list. Defaults to None. 18 next_node (NodeCore, optional): Node class used to create list nodes. Defaults to Node. 19 """ 20 self._value = value 21 self._next_node = next_node 22 23 @property 24 def value(self): 25 """Any: The stored value in the `Node` object.""" 26 return self._value 27 28 @property 29 def next_node(self): 30 """Return the next node. If there are no next node, returns `None`.""" 31 return self._next_node 32 33 @next_node.setter 34 def next_node(self, node): 35 """Set the next node. 36 37 Args: 38 node (Node | None): new `Node` object or None if it's the tail node. 39 """ 40 self._next_node = node
This is the fundamental class that's used & inherited for linear data structures.
Attributes:
- value (any): Value (data) stored in node.
- next_node (Node | None): A pointer to the next node if exists.
13 def __init__(self, value, next_node=None): 14 """Initialize a NodeCore object. 15 16 Args: 17 value (Any, optional): First value to add to the list. Defaults to None. 18 next_node (NodeCore, optional): Node class used to create list nodes. Defaults to Node. 19 """ 20 self._value = value 21 self._next_node = next_node
Initialize a NodeCore object.
Arguments:
- value (Any, optional): First value to add to the list. Defaults to None.
- next_node (NodeCore, optional): Node class used to create list nodes. Defaults to Node.
43class NodePriorityQueue(NodeCore): 44 """This is the base node used to construct a priority queue. Inherits the NodeCore. 45 46 Attributes: 47 value (any): Value (data) stored in node. 48 priority (int): Priority value for each node. 49 next_node (Node | None): Next node for the current node. 50 """ 51 52 def __init__(self, value, priority=None, next_node=None): 53 """Initialize a NodePriorityQueue object. This is used for creating priority queues. Inherits NodeCore. 54 55 Args: 56 value (Any, optional): First value to add to the list. Defaults to None. 57 next_node (type, optional): Node class used to create list nodes. Defaults to Node. 58 priority (int): The priority value for the node. This will set the position of the node in the list. 59 """ 60 super().__init__(value, next_node) 61 self.priority = priority
This is the base node used to construct a priority queue. Inherits the NodeCore.
Attributes:
- value (any): Value (data) stored in node.
- priority (int): Priority value for each node.
- next_node (Node | None): Next node for the current node.
52 def __init__(self, value, priority=None, next_node=None): 53 """Initialize a NodePriorityQueue object. This is used for creating priority queues. Inherits NodeCore. 54 55 Args: 56 value (Any, optional): First value to add to the list. Defaults to None. 57 next_node (type, optional): Node class used to create list nodes. Defaults to Node. 58 priority (int): The priority value for the node. This will set the position of the node in the list. 59 """ 60 super().__init__(value, next_node) 61 self.priority = priority
Initialize a NodePriorityQueue object. This is used for creating priority queues. Inherits NodeCore.
Arguments:
- value (Any, optional): First value to add to the list. Defaults to None.
- next_node (type, optional): Node class used to create list nodes. Defaults to Node.
- priority (int): The priority value for the node. This will set the position of the node in the list.
64class TreeNode: 65 """This is the base node used when implementing binary search trees. 66 67 Args: 68 value (Any): The stored value in the node. 69 left_child (Node | None): Left child of the current node. 70 right_child (Node | None): Right child of the current node. 71 parent (Node | None): Parent node of the current node. 72 """ 73 74 def __init__(self, value=None, left_child=None, right_child=None, parent=None): 75 """Initialize a TreeNode object. Used in data_structures.BinarySearchTree. 76 77 Args: 78 value (Any): The stored value in the node. 79 left_child (Node | None): Left child of the current node. 80 right_child (Node | None): Right child of the current node. 81 parent (Node | None): Parent node of the current node. 82 """ 83 self._value = value 84 self._left_child = left_child 85 self._right_child = right_child 86 self._parent = parent 87 88 @property 89 def value(self): 90 """Any: The stored value in the `TreeNode` object.""" 91 return self._value 92 93 @value.setter 94 def value(self, value): 95 """Set a new value for the node. 96 97 Args: 98 value (Any): The new value of the node. 99 """ 100 self._value = value 101 102 @property 103 def left_child(self): 104 """None | TreeNode: Returns left child the TreeNode object if exists.""" 105 return self._left_child 106 107 @left_child.setter 108 def left_child(self, left_child): 109 """Set the left_child of the TreeNode. 110 111 Args: 112 left_child (TreeNode | None): Left child of the node object. 113 """ 114 self._left_child = left_child 115 116 @property 117 def right_child(self): 118 """TreeNode: Returns the right child TreeNode object.""" 119 return self._right_child 120 121 @right_child.setter 122 def right_child(self, right_child): 123 """Set the right_child of the TreeNode. 124 125 Args: 126 right_child (TreeNode | None): Right child of the node object. 127 """ 128 self._right_child = right_child 129 130 @property 131 def left_value(self): 132 """Any: Value of the left_child node.""" 133 return self._left_child.value if self._left_child else None 134 135 @property 136 def right_value(self): 137 """Any: Value of the right_child node.""" 138 return self._right_child.value if self._right_child else None 139 140 @property 141 def parent(self): 142 """None | TreeNode: Returns the parent node (TreeNode) object if exists.""" 143 return self._parent 144 145 @parent.setter 146 def parent(self, parent): 147 """Set the parent node. 148 149 Args: 150 parent (TreeNode | None): Parent of the node object. 151 """ 152 self._parent = parent
This is the base node used when implementing binary search trees.
Arguments:
- value (Any): The stored value in the node.
- left_child (Node | None): Left child of the current node.
- right_child (Node | None): Right child of the current node.
- parent (Node | None): Parent node of the current node.
74 def __init__(self, value=None, left_child=None, right_child=None, parent=None): 75 """Initialize a TreeNode object. Used in data_structures.BinarySearchTree. 76 77 Args: 78 value (Any): The stored value in the node. 79 left_child (Node | None): Left child of the current node. 80 right_child (Node | None): Right child of the current node. 81 parent (Node | None): Parent node of the current node. 82 """ 83 self._value = value 84 self._left_child = left_child 85 self._right_child = right_child 86 self._parent = parent
Initialize a TreeNode object. Used in data_structures.BinarySearchTree.
Arguments:
- value (Any): The stored value in the node.
- left_child (Node | None): Left child of the current node.
- right_child (Node | None): Right child of the current node.
- parent (Node | None): Parent node of the current node.
88 @property 89 def value(self): 90 """Any: The stored value in the `TreeNode` object.""" 91 return self._value
Any: The stored value in the TreeNode object.
102 @property 103 def left_child(self): 104 """None | TreeNode: Returns left child the TreeNode object if exists.""" 105 return self._left_child
None | TreeNode: Returns left child the TreeNode object if exists.
116 @property 117 def right_child(self): 118 """TreeNode: Returns the right child TreeNode object.""" 119 return self._right_child
TreeNode: Returns the right child TreeNode object.
130 @property 131 def left_value(self): 132 """Any: Value of the left_child node.""" 133 return self._left_child.value if self._left_child else None
Any: Value of the left_child node.
7class LinkedList: 8 """This is the singly linked list implementation. 9 10 Attributes: 11 _Node (Any): This is the node object used to create a list. Custom node objects can be used. Defaults to NodeCore. 12 head_node (None or Node): The first node in the list. 13 tail_node (None or Node): The last node in the list. 14 size (int): The number of elements in the list. 15 value (Any): Value of the node. 16 """ 17 18 def __init__(self, value=None, node=Node): 19 """Initialize the `LinkedList`. 20 21 Args: 22 head_node (None or Node): The first node in the list. 23 tail_node (None or Node): The last node in the list. 24 size (int): The number of elements in the list. 25 value (Any): Value of the node. 26 node (any | Node): Node container to construct a doubly linked list. Defaults to Node. 27 """ 28 self._Node = node 29 if value is not None: 30 self.head_node = self._Node(value) 31 self.tail_node = self.head_node 32 self._size = 1 33 else: 34 self.head_node = None 35 self.tail_node = None 36 self._size = 0 37 38 @property 39 def size(self): 40 """int: How many elements in the linked list.""" 41 return self._size 42 43 def __str__(self): 44 """str: A string representation of nodes in the list.""" 45 return "[ " + " ".join(str(node) for node in self) + " ]" 46 47 def __len__(self): 48 """int: How many elements in the linked list.""" 49 return self._size 50 51 def __getitem__(self, index): 52 """Make LinkedList class indexable. 53 54 Args: 55 index (int): Index of `LinkedList` object to return the value at that position. 56 57 Raises: 58 IndexError: If the `index` is out of range. 59 """ 60 if index < 0: 61 index += self._size 62 if not 0 <= index < self._size: 63 raise IndexError(f"Index {index} is out of bounds") 64 else: 65 return self.find_by_index(index) 66 67 def __iter__(self): 68 """Dunder method to make `LinkedList` iterable.""" 69 current_node = self.head_node 70 while current_node: 71 yield current_node.value 72 current_node = current_node.next_node 73 74 def insert_head(self, value): 75 """Inserts a value at head. Wraps the value inside the Node object. 76 77 Args: 78 value (any): Value to be inserted. 79 """ 80 new_node = self._Node(value) 81 new_node.next_node = self.head_node 82 self._size += 1 83 self.head_node = new_node 84 if self._size == 1: 85 self.tail_node = new_node 86 87 def insert_tail(self, value): 88 """Inserts a value at tail. Wraps the value inside the Node object. 89 90 Args: 91 value (any): Value to be inserted. 92 """ 93 new_node = self._Node(value) 94 if self._size == 0: 95 self.head_node = new_node 96 self.tail_node = self.head_node 97 else: 98 self.tail_node.next_node = new_node 99 self.tail_node = new_node 100 self._size += 1 101 102 def insert_index(self, index, value): 103 """Insert the value inside the given index. Shifts the elements coming after that index. 104 105 Args: 106 index (int): Index position to insert the node. 107 value (any): Value wrapped inside a Node to be inserted. 108 109 Raises: 110 IndexError: If the `index` is out of range. 111 """ 112 if index < 0 or index > self._size - 1: 113 raise IndexError(f"Index {index} out of bounds for insertion") 114 else: 115 if index == 0: 116 self.insert_head(value) 117 return 118 current_node = self.head_node 119 counter = 0 120 while counter < index - 1: 121 current_node = current_node.next_node 122 counter += 1 123 new_node = self._Node(value) 124 next_node = current_node.next_node 125 current_node.next_node = new_node 126 self._size += 1 127 new_node.next_node = next_node 128 129 def delete_head(self): 130 """Delete the first element in the list. 131 132 Raises: 133 Exception: If the list is empty. 134 """ 135 if self.head_node is None: 136 raise Exception("Cannot delete from an empty list") 137 second_node = self.head_node.next_node 138 self._size -= 1 139 self.head_node = second_node 140 141 def delete_tail(self): 142 """Delete the last element in the list. 143 144 Raises: 145 Exception: If the `head_node` is None. 146 """ 147 if self.head_node is None: 148 raise Exception("Cannot delete from an empty list") 149 elif self._size == 1: 150 self.head_node = None 151 self.tail_node = None 152 else: 153 current_node = self.head_node 154 while current_node.next_node.next_node is not None: 155 current_node = current_node.next_node 156 self.tail_node = current_node 157 current_node.next_node = None 158 self._size -= 1 159 160 def delete_index(self, index): 161 """Delete the element at the given index. 162 163 Args: 164 index (int): The index where the node will be deleted. 165 166 Raises: 167 IndexError: If the `index` is out of range. 168 """ 169 if index < 0 or index > self._size - 1: 170 raise IndexError( 171 f"Index {index} is out of bounds for deletion - List size: {self._size}" 172 ) 173 current_node = self.head_node 174 counter = 0 175 if self._size == 1 or index == 0: 176 self.delete_head() 177 else: 178 while ( 179 counter < index - 1 180 ): # get to the previous node of the node we want to delete 181 current_node = current_node.next_node 182 counter += 1 183 next_node = current_node.next_node.next_node 184 current_node.next_node = next_node 185 self._size -= 1 186 187 def delete_value(self, value): 188 """Delete a node with the given value. 189 190 Args: 191 value (any): Value that a node contains. 192 193 Raises: 194 Exception: If the list is empty. 195 ValueError: If the `value` is not found in the list. 196 """ 197 current_node = self.head_node 198 previous_node = None 199 200 while current_node is not None: 201 if current_node.value == value: 202 if previous_node is None: 203 self.delete_head() 204 return 205 else: 206 previous_node.next_node = current_node.next_node 207 self._size -= 1 208 return 209 previous_node = current_node 210 current_node = current_node.next_node 211 212 raise ValueError(f"{value} is not in the list") 213 214 def find_by_index(self, index): 215 """Find and return the value of a node of a given index. 216 217 Args: 218 index (int): The index of the node whose value is wanted. 219 220 Returns: 221 any: value of the node at `index` 222 223 Raises: 224 IndexError: If the `index` is out of range. 225 """ 226 if index < 0: 227 index += self._size 228 if index > self._size - 1: 229 raise IndexError(f"Index {index} is out of bounds for check") 230 current_node = self.head_node 231 counter = 0 232 while current_node is not None: 233 if counter == index: 234 return current_node.value 235 else: 236 current_node = current_node.next_node 237 counter += 1 238 239 def traverse(self): 240 """Traverse and print the each value in the list.""" 241 current_node = self.head_node 242 while current_node is not None: 243 print(current_node.value) 244 current_node = current_node.next_node
This is the singly linked list implementation.
Attributes:
- _Node (Any): This is the node object used to create a list. Custom node objects can be used. Defaults to NodeCore.
- head_node (None or Node): The first node in the list.
- tail_node (None or Node): The last node in the list.
- size (int): The number of elements in the list.
- value (Any): Value of the node.
18 def __init__(self, value=None, node=Node): 19 """Initialize the `LinkedList`. 20 21 Args: 22 head_node (None or Node): The first node in the list. 23 tail_node (None or Node): The last node in the list. 24 size (int): The number of elements in the list. 25 value (Any): Value of the node. 26 node (any | Node): Node container to construct a doubly linked list. Defaults to Node. 27 """ 28 self._Node = node 29 if value is not None: 30 self.head_node = self._Node(value) 31 self.tail_node = self.head_node 32 self._size = 1 33 else: 34 self.head_node = None 35 self.tail_node = None 36 self._size = 0
Initialize the LinkedList.
Arguments:
- head_node (None or Node): The first node in the list.
- tail_node (None or Node): The last node in the list.
- size (int): The number of elements in the list.
- value (Any): Value of the node.
- node (any | Node): Node container to construct a doubly linked list. Defaults to Node.
38 @property 39 def size(self): 40 """int: How many elements in the linked list.""" 41 return self._size
int: How many elements in the linked list.
74 def insert_head(self, value): 75 """Inserts a value at head. Wraps the value inside the Node object. 76 77 Args: 78 value (any): Value to be inserted. 79 """ 80 new_node = self._Node(value) 81 new_node.next_node = self.head_node 82 self._size += 1 83 self.head_node = new_node 84 if self._size == 1: 85 self.tail_node = new_node
Inserts a value at head. Wraps the value inside the Node object.
Arguments:
- value (any): Value to be inserted.
87 def insert_tail(self, value): 88 """Inserts a value at tail. Wraps the value inside the Node object. 89 90 Args: 91 value (any): Value to be inserted. 92 """ 93 new_node = self._Node(value) 94 if self._size == 0: 95 self.head_node = new_node 96 self.tail_node = self.head_node 97 else: 98 self.tail_node.next_node = new_node 99 self.tail_node = new_node 100 self._size += 1
Inserts a value at tail. Wraps the value inside the Node object.
Arguments:
- value (any): Value to be inserted.
102 def insert_index(self, index, value): 103 """Insert the value inside the given index. Shifts the elements coming after that index. 104 105 Args: 106 index (int): Index position to insert the node. 107 value (any): Value wrapped inside a Node to be inserted. 108 109 Raises: 110 IndexError: If the `index` is out of range. 111 """ 112 if index < 0 or index > self._size - 1: 113 raise IndexError(f"Index {index} out of bounds for insertion") 114 else: 115 if index == 0: 116 self.insert_head(value) 117 return 118 current_node = self.head_node 119 counter = 0 120 while counter < index - 1: 121 current_node = current_node.next_node 122 counter += 1 123 new_node = self._Node(value) 124 next_node = current_node.next_node 125 current_node.next_node = new_node 126 self._size += 1 127 new_node.next_node = next_node
Insert the value inside the given index. Shifts the elements coming after that index.
Arguments:
- index (int): Index position to insert the node.
- value (any): Value wrapped inside a Node to be inserted.
Raises:
- IndexError: If the
indexis out of range.
129 def delete_head(self): 130 """Delete the first element in the list. 131 132 Raises: 133 Exception: If the list is empty. 134 """ 135 if self.head_node is None: 136 raise Exception("Cannot delete from an empty list") 137 second_node = self.head_node.next_node 138 self._size -= 1 139 self.head_node = second_node
Delete the first element in the list.
Raises:
- Exception: If the list is empty.
141 def delete_tail(self): 142 """Delete the last element in the list. 143 144 Raises: 145 Exception: If the `head_node` is None. 146 """ 147 if self.head_node is None: 148 raise Exception("Cannot delete from an empty list") 149 elif self._size == 1: 150 self.head_node = None 151 self.tail_node = None 152 else: 153 current_node = self.head_node 154 while current_node.next_node.next_node is not None: 155 current_node = current_node.next_node 156 self.tail_node = current_node 157 current_node.next_node = None 158 self._size -= 1
Delete the last element in the list.
Raises:
- Exception: If the
head_nodeis None.
160 def delete_index(self, index): 161 """Delete the element at the given index. 162 163 Args: 164 index (int): The index where the node will be deleted. 165 166 Raises: 167 IndexError: If the `index` is out of range. 168 """ 169 if index < 0 or index > self._size - 1: 170 raise IndexError( 171 f"Index {index} is out of bounds for deletion - List size: {self._size}" 172 ) 173 current_node = self.head_node 174 counter = 0 175 if self._size == 1 or index == 0: 176 self.delete_head() 177 else: 178 while ( 179 counter < index - 1 180 ): # get to the previous node of the node we want to delete 181 current_node = current_node.next_node 182 counter += 1 183 next_node = current_node.next_node.next_node 184 current_node.next_node = next_node 185 self._size -= 1
Delete the element at the given index.
Arguments:
- index (int): The index where the node will be deleted.
Raises:
- IndexError: If the
indexis out of range.
187 def delete_value(self, value): 188 """Delete a node with the given value. 189 190 Args: 191 value (any): Value that a node contains. 192 193 Raises: 194 Exception: If the list is empty. 195 ValueError: If the `value` is not found in the list. 196 """ 197 current_node = self.head_node 198 previous_node = None 199 200 while current_node is not None: 201 if current_node.value == value: 202 if previous_node is None: 203 self.delete_head() 204 return 205 else: 206 previous_node.next_node = current_node.next_node 207 self._size -= 1 208 return 209 previous_node = current_node 210 current_node = current_node.next_node 211 212 raise ValueError(f"{value} is not in the list")
Delete a node with the given value.
Arguments:
- value (any): Value that a node contains.
Raises:
- Exception: If the list is empty.
- ValueError: If the
valueis not found in the list.
214 def find_by_index(self, index): 215 """Find and return the value of a node of a given index. 216 217 Args: 218 index (int): The index of the node whose value is wanted. 219 220 Returns: 221 any: value of the node at `index` 222 223 Raises: 224 IndexError: If the `index` is out of range. 225 """ 226 if index < 0: 227 index += self._size 228 if index > self._size - 1: 229 raise IndexError(f"Index {index} is out of bounds for check") 230 current_node = self.head_node 231 counter = 0 232 while current_node is not None: 233 if counter == index: 234 return current_node.value 235 else: 236 current_node = current_node.next_node 237 counter += 1
Find and return the value of a node of a given index.
Arguments:
- index (int): The index of the node whose value is wanted.
Returns:
any: value of the node at
index
Raises:
- IndexError: If the
indexis out of range.
7class Node(NodeCore): 8 """Base node for DoublyLinkedList. Inherits all attributes from NodeCore. 9 10 Attributes: 11 prev_node (None | Node): Pointer to the previous node. 12 """ 13 14 def __init__(self, value, next_node=None, prev_node=None): 15 """Initialize a Node. 16 17 Args: 18 value 19 value (any): Value (data) stored in node. 20 next_node (Node | None): A pointer to the next node if exists. 21 prev_node (Node | None): A pointer to the previous node if exists. 22 """ 23 super().__init__(value, next_node) 24 self._prev_node = prev_node 25 26 @property 27 def prev_node(self): 28 """Node | None: Return the previous node.""" 29 return self._prev_node 30 31 @prev_node.setter 32 def prev_node(self, value): 33 """Set the previous node's value. 34 35 Args: 36 value (any): Value of the previous `Node` object. 37 """ 38 self._prev_node = value
Base node for DoublyLinkedList. Inherits all attributes from NodeCore.
Attributes:
- prev_node (None | Node): Pointer to the previous node.
14 def __init__(self, value, next_node=None, prev_node=None): 15 """Initialize a Node. 16 17 Args: 18 value 19 value (any): Value (data) stored in node. 20 next_node (Node | None): A pointer to the next node if exists. 21 prev_node (Node | None): A pointer to the previous node if exists. 22 """ 23 super().__init__(value, next_node) 24 self._prev_node = prev_node
Initialize a Node.
Arguments:
- value
- value (any): Value (data) stored in node.
- next_node (Node | None): A pointer to the next node if exists.
- prev_node (Node | None): A pointer to the previous node if exists.
41class DoublyLinkedList: 42 """This is the doubly linked list implementation. 43 44 Attributes: 45 _Node (Any): This is the node object used to create a list. Custom node objects can be used. Defaults to NodeCore. 46 head_node (None or Node): The first node in the list. 47 tail_node (None or Node): The last node in the list. 48 size (int): The number of elements in the list. 49 value (Any): Value of the node. 50 """ 51 52 def __init__(self, value=None, node=Node): 53 """Initialize the `DoublyLinkedList`. 54 55 Args: 56 head_node (None or Node): The first node in the list. 57 tail_node (None or Node): The last node in the list. 58 size (int): The number of elements in the list. 59 value (Any): Value of the node. 60 node (any | Node): Node container to construct a doubly linked list. Defaults to Node. 61 """ 62 self._Node = node 63 if value is not None: 64 self.head_node = self._Node(value) 65 self.tail_node = self.head_node 66 self._size = 1 67 else: 68 self.head_node = None 69 self.tail_node = None 70 self._size = 0 71 72 def __str__(self): 73 """Return the string representation of the `LinkedList` object.""" 74 return "[ " + " ".join(str(node) for node in self) + " ]" 75 76 def __len__(self): 77 """Return the size of the linked list.""" 78 return self._size 79 80 def __getitem__(self, index): 81 """Make LinkedList class indexable. 82 83 Args: 84 index (int): Index of `LinkedList` object to return the value at that position. 85 86 Raises: 87 IndexError: If the `index` is out of range. 88 """ 89 if index < 0: 90 index += self._size 91 if not 0 <= index < self._size: 92 raise IndexError(f"Index {index} is out of bounds") 93 else: 94 return self.find_by_index(index) 95 96 def __iter__(self): 97 """Dunder method to make `LinkedList` iterable.""" 98 current_node = self.head_node 99 while current_node: 100 yield current_node.value 101 current_node = current_node.next_node 102 103 def insert_head(self, value): 104 """Inserts a value at head. Wraps the value inside the Node object. 105 106 Args: 107 value (any): Value to be inserted. 108 """ 109 if self._size == 0: 110 self.head_node = self._Node(value) 111 self.tail_node = self.head_node 112 else: 113 new_node = self._Node(value) 114 new_node.next_node = self.head_node 115 self.head_node.prev_node = new_node 116 self.head_node = new_node 117 self._size += 1 118 119 def insert_tail(self, value): 120 """Inserts a value at tail. Wraps the value inside the Node object. 121 122 Args: 123 value (any): Value to be inserted. 124 """ 125 new_node = self._Node(value) 126 if self._size == 0: 127 self.head_node = new_node 128 self.tail_node = self.head_node 129 else: 130 self.tail_node.next_node = new_node 131 final_node = self.tail_node.next_node 132 final_node.prev_node = self.tail_node 133 self.tail_node = final_node 134 self._size += 1 135 136 def delete_head(self): 137 """Delete the first element in the list. 138 139 Raises: 140 Exception: If the list is empty. 141 """ 142 if self._size == 0: 143 raise Exception("Cannot delete from an empty list") 144 elif self._size == 1: 145 self.head_node = None 146 self.tail_node = None 147 else: 148 self.head_node = self.head_node.next_node 149 self.head_node.prev_node = None 150 self._size -= 1 151 152 def delete_tail(self): 153 """Delete the last element in the list. 154 155 Raises: 156 Exception: If the size is 0. 157 """ 158 if self._size == 0: 159 raise Exception("Cannot delete from an empty list") 160 elif self._size == 1: 161 self.head_node = None 162 self.tail_node = None 163 else: 164 self.tail_node = self.tail_node.prev_node 165 self.tail_node.next_node = None 166 self._size -= 1 167 168 def delete_index(self, index): 169 """Delete the element at the given index. 170 171 Args: 172 index (int): The index where the node will be deleted. 173 174 Raises: 175 IndexError: If the `index` is out of range. 176 """ 177 if index < 0 or index > self._size - 1: 178 raise IndexError(f"Index {index} is out of bounds") 179 current_node = self.head_node 180 counter = 0 181 if self._size == 1 or index == 0: 182 self.delete_head() 183 else: 184 while counter < index - 1: 185 current_node = current_node.next_node 186 counter += 1 187 next_node = current_node.next_node.next_node 188 current_node.next_node = next_node 189 if next_node is None: 190 self.tail_node = current_node 191 else: 192 next_node.prev_node = current_node 193 self._size -= 1 194 return 195 196 def delete_value(self, value): 197 """Delete a node with the given value. 198 199 Args: 200 value (any): Value that a node contains. 201 202 Raises: 203 Exception: If the list is empty. 204 ValueError: If the `value` is not found in the list. 205 """ 206 if self._size == 0: 207 raise Exception(f"Cannot delete from an empty list") 208 else: 209 current_node = self.head_node 210 prev_node = None 211 while current_node is not None: 212 if current_node.value == value: 213 if prev_node is None: # if it's the first node 214 self.delete_head() 215 else: 216 next_node = current_node.next_node 217 prev_node.next_node = next_node 218 if next_node is None: 219 self.tail_node = prev_node 220 else: 221 next_node.prev_node = prev_node 222 self._size -= 1 223 return 224 prev_node = current_node 225 current_node = current_node.next_node 226 raise ValueError(f"{value} cannot be found in the list") 227 228 def find_by_index(self, index): 229 """Find and return the value of a node of a given index. 230 231 Args: 232 index (int): The index of the node whose value is wanted. 233 234 Returns: 235 any: value of the node at `index` 236 237 Raises: 238 IndexError: If the `index` is out of range. 239 """ 240 if index < 0 or index > self._size - 1: 241 raise IndexError(f"Index {index} is out of bounds") 242 counter = 0 243 current_node = self.head_node 244 while current_node is not None: 245 if counter == index: 246 return current_node.value 247 current_node = current_node.next_node 248 counter += 1 249 250 def find_by_value(self, value): 251 """Find the first occurence of a node by value. 252 253 Args: 254 value (any): Value of the node. 255 256 Raises: 257 Exception: If the list is empty. 258 ValueError: If the `value` is not found in the list. 259 """ 260 if self._size == 0: 261 raise Exception("Cannot find a value in an empty list.") 262 else: 263 current_node = self.head_node 264 idx = 0 265 while current_node is not None: 266 if current_node.value == value: 267 print(f"{value} is found at index {idx}") 268 return 269 else: 270 current_node = current_node.next_node 271 idx += 1 272 raise ValueError(f"{value} cannot be found in the list") 273 274 def reverse_find_by_value(self, value): 275 """Find the last occurence of the value. Starts traversing from the tail node. 276 277 Args: 278 value (any): Value of the node. 279 280 Raises: 281 Exception: If the `value` isn't found in the list. 282 """ 283 if self._size == 0: 284 raise Exception("Cannot find a value in an empty list.") 285 current_node = self.tail_node 286 reverse_idx = 0 287 while current_node is not None: 288 if current_node.value == value: 289 print(f"{value} is found at index {self._size - reverse_idx - 1}") 290 return 291 current_node = current_node.prev_node 292 reverse_idx += 1 293 raise Exception("f{value} cannot be found in the list") 294 295 def traverse(self): 296 """Traverse the list and print each value.""" 297 current_node = self.head_node 298 while current_node is not None: 299 print(current_node.value) 300 current_node = current_node.next_node
This is the doubly linked list implementation.
Attributes:
- _Node (Any): This is the node object used to create a list. Custom node objects can be used. Defaults to NodeCore.
- head_node (None or Node): The first node in the list.
- tail_node (None or Node): The last node in the list.
- size (int): The number of elements in the list.
- value (Any): Value of the node.
52 def __init__(self, value=None, node=Node): 53 """Initialize the `DoublyLinkedList`. 54 55 Args: 56 head_node (None or Node): The first node in the list. 57 tail_node (None or Node): The last node in the list. 58 size (int): The number of elements in the list. 59 value (Any): Value of the node. 60 node (any | Node): Node container to construct a doubly linked list. Defaults to Node. 61 """ 62 self._Node = node 63 if value is not None: 64 self.head_node = self._Node(value) 65 self.tail_node = self.head_node 66 self._size = 1 67 else: 68 self.head_node = None 69 self.tail_node = None 70 self._size = 0
Initialize the DoublyLinkedList.
Arguments:
- head_node (None or Node): The first node in the list.
- tail_node (None or Node): The last node in the list.
- size (int): The number of elements in the list.
- value (Any): Value of the node.
- node (any | Node): Node container to construct a doubly linked list. Defaults to Node.
103 def insert_head(self, value): 104 """Inserts a value at head. Wraps the value inside the Node object. 105 106 Args: 107 value (any): Value to be inserted. 108 """ 109 if self._size == 0: 110 self.head_node = self._Node(value) 111 self.tail_node = self.head_node 112 else: 113 new_node = self._Node(value) 114 new_node.next_node = self.head_node 115 self.head_node.prev_node = new_node 116 self.head_node = new_node 117 self._size += 1
Inserts a value at head. Wraps the value inside the Node object.
Arguments:
- value (any): Value to be inserted.
119 def insert_tail(self, value): 120 """Inserts a value at tail. Wraps the value inside the Node object. 121 122 Args: 123 value (any): Value to be inserted. 124 """ 125 new_node = self._Node(value) 126 if self._size == 0: 127 self.head_node = new_node 128 self.tail_node = self.head_node 129 else: 130 self.tail_node.next_node = new_node 131 final_node = self.tail_node.next_node 132 final_node.prev_node = self.tail_node 133 self.tail_node = final_node 134 self._size += 1
Inserts a value at tail. Wraps the value inside the Node object.
Arguments:
- value (any): Value to be inserted.
136 def delete_head(self): 137 """Delete the first element in the list. 138 139 Raises: 140 Exception: If the list is empty. 141 """ 142 if self._size == 0: 143 raise Exception("Cannot delete from an empty list") 144 elif self._size == 1: 145 self.head_node = None 146 self.tail_node = None 147 else: 148 self.head_node = self.head_node.next_node 149 self.head_node.prev_node = None 150 self._size -= 1
Delete the first element in the list.
Raises:
- Exception: If the list is empty.
152 def delete_tail(self): 153 """Delete the last element in the list. 154 155 Raises: 156 Exception: If the size is 0. 157 """ 158 if self._size == 0: 159 raise Exception("Cannot delete from an empty list") 160 elif self._size == 1: 161 self.head_node = None 162 self.tail_node = None 163 else: 164 self.tail_node = self.tail_node.prev_node 165 self.tail_node.next_node = None 166 self._size -= 1
Delete the last element in the list.
Raises:
- Exception: If the size is 0.
168 def delete_index(self, index): 169 """Delete the element at the given index. 170 171 Args: 172 index (int): The index where the node will be deleted. 173 174 Raises: 175 IndexError: If the `index` is out of range. 176 """ 177 if index < 0 or index > self._size - 1: 178 raise IndexError(f"Index {index} is out of bounds") 179 current_node = self.head_node 180 counter = 0 181 if self._size == 1 or index == 0: 182 self.delete_head() 183 else: 184 while counter < index - 1: 185 current_node = current_node.next_node 186 counter += 1 187 next_node = current_node.next_node.next_node 188 current_node.next_node = next_node 189 if next_node is None: 190 self.tail_node = current_node 191 else: 192 next_node.prev_node = current_node 193 self._size -= 1 194 return
Delete the element at the given index.
Arguments:
- index (int): The index where the node will be deleted.
Raises:
- IndexError: If the
indexis out of range.
196 def delete_value(self, value): 197 """Delete a node with the given value. 198 199 Args: 200 value (any): Value that a node contains. 201 202 Raises: 203 Exception: If the list is empty. 204 ValueError: If the `value` is not found in the list. 205 """ 206 if self._size == 0: 207 raise Exception(f"Cannot delete from an empty list") 208 else: 209 current_node = self.head_node 210 prev_node = None 211 while current_node is not None: 212 if current_node.value == value: 213 if prev_node is None: # if it's the first node 214 self.delete_head() 215 else: 216 next_node = current_node.next_node 217 prev_node.next_node = next_node 218 if next_node is None: 219 self.tail_node = prev_node 220 else: 221 next_node.prev_node = prev_node 222 self._size -= 1 223 return 224 prev_node = current_node 225 current_node = current_node.next_node 226 raise ValueError(f"{value} cannot be found in the list")
Delete a node with the given value.
Arguments:
- value (any): Value that a node contains.
Raises:
- Exception: If the list is empty.
- ValueError: If the
valueis not found in the list.
228 def find_by_index(self, index): 229 """Find and return the value of a node of a given index. 230 231 Args: 232 index (int): The index of the node whose value is wanted. 233 234 Returns: 235 any: value of the node at `index` 236 237 Raises: 238 IndexError: If the `index` is out of range. 239 """ 240 if index < 0 or index > self._size - 1: 241 raise IndexError(f"Index {index} is out of bounds") 242 counter = 0 243 current_node = self.head_node 244 while current_node is not None: 245 if counter == index: 246 return current_node.value 247 current_node = current_node.next_node 248 counter += 1
Find and return the value of a node of a given index.
Arguments:
- index (int): The index of the node whose value is wanted.
Returns:
any: value of the node at
index
Raises:
- IndexError: If the
indexis out of range.
250 def find_by_value(self, value): 251 """Find the first occurence of a node by value. 252 253 Args: 254 value (any): Value of the node. 255 256 Raises: 257 Exception: If the list is empty. 258 ValueError: If the `value` is not found in the list. 259 """ 260 if self._size == 0: 261 raise Exception("Cannot find a value in an empty list.") 262 else: 263 current_node = self.head_node 264 idx = 0 265 while current_node is not None: 266 if current_node.value == value: 267 print(f"{value} is found at index {idx}") 268 return 269 else: 270 current_node = current_node.next_node 271 idx += 1 272 raise ValueError(f"{value} cannot be found in the list")
Find the first occurence of a node by value.
Arguments:
- value (any): Value of the node.
Raises:
- Exception: If the list is empty.
- ValueError: If the
valueis not found in the list.
274 def reverse_find_by_value(self, value): 275 """Find the last occurence of the value. Starts traversing from the tail node. 276 277 Args: 278 value (any): Value of the node. 279 280 Raises: 281 Exception: If the `value` isn't found in the list. 282 """ 283 if self._size == 0: 284 raise Exception("Cannot find a value in an empty list.") 285 current_node = self.tail_node 286 reverse_idx = 0 287 while current_node is not None: 288 if current_node.value == value: 289 print(f"{value} is found at index {self._size - reverse_idx - 1}") 290 return 291 current_node = current_node.prev_node 292 reverse_idx += 1 293 raise Exception("f{value} cannot be found in the list")
Find the last occurence of the value. Starts traversing from the tail node.
Arguments:
- value (any): Value of the node.
Raises:
- Exception: If the
valueisn't found in the list.
10class QueueArray: 11 """Queue data structure using basic arrays. 12 13 Attributes: 14 array (List): Empty array. This will contain the data. 15 """ 16 17 def __init__(self, *args): 18 """Initialize a QueueArray. 19 20 The constructor will accept a variable number of elements and enqueue each 21 element into the array. 22 23 Args: 24 *args: Variable length argument list. Those are basically elements to be stored. 25 """ 26 self.array = [] 27 for arg in args: 28 self.enqueue(arg) 29 30 @property 31 def size(self): 32 """int: Size of the array.""" 33 return len(self.array) 34 35 @property 36 def front(self): 37 """any: The value at front if the queue is not empty. Otherwise None.""" 38 return self.array[0] if not self.is_empty() else None 39 40 @property 41 def rear(self): 42 """any: The value at the rear if the queue is not empty. Otherwise None.""" 43 return self.array[-1] if not self.is_empty() else None 44 45 def __str__(self): 46 """str: String representation of the QueueArray.""" 47 return "[ " + " ".join(str(element) for element in self.array) + " ]" 48 49 def __len__(self): 50 """int: Size of the QueueArray.""" 51 return self.size 52 53 def __getitem__(self, index): 54 """Dunder method to make QueueArray indexable. 55 56 Args: 57 index (int): Index of `LinkedList` object to return the value at that position. 58 59 Raises: 60 IndexError: If the `index` is out of range. 61 """ 62 if index < 0: 63 index += self.size 64 if not 0 <= index < self.size: 65 raise IndexError(f"Index {index} is out of bounds") 66 else: 67 return self.array[index] 68 69 def enqueue(self, value): 70 """Enqueue the element inside the array. 71 72 Args: 73 value (any): Value to be enqueued. 74 """ 75 self.array.append(value) 76 77 def dequeue(self): # O(n) 78 """Dequeue the element from the array. 79 80 Since this operation is using `.pop(0)`, it has O(n) time complexity because 81 all the values in the array is shifted. 82 83 Args: 84 value (any): Value to be dequeued. 85 """ 86 return self.array.pop(0) 87 88 def is_empty(self): 89 """bool: Return True if the size is 0, else False.""" 90 return self.size == 0 91 92 def clear(self): 93 """Clears the whole array of the QueueArray.""" 94 self.array.clear() 95 96 def copy(self): 97 """Return a deep copy of the QueueArray object with the same values in the same order.""" 98 return QueueArray(*self.array)
Queue data structure using basic arrays.
Attributes:
- array (List): Empty array. This will contain the data.
17 def __init__(self, *args): 18 """Initialize a QueueArray. 19 20 The constructor will accept a variable number of elements and enqueue each 21 element into the array. 22 23 Args: 24 *args: Variable length argument list. Those are basically elements to be stored. 25 """ 26 self.array = [] 27 for arg in args: 28 self.enqueue(arg)
Initialize a QueueArray.
The constructor will accept a variable number of elements and enqueue each element into the array.
Arguments:
- *args: Variable length argument list. Those are basically elements to be stored.
35 @property 36 def front(self): 37 """any: The value at front if the queue is not empty. Otherwise None.""" 38 return self.array[0] if not self.is_empty() else None
any: The value at front if the queue is not empty. Otherwise None.
40 @property 41 def rear(self): 42 """any: The value at the rear if the queue is not empty. Otherwise None.""" 43 return self.array[-1] if not self.is_empty() else None
any: The value at the rear if the queue is not empty. Otherwise None.
69 def enqueue(self, value): 70 """Enqueue the element inside the array. 71 72 Args: 73 value (any): Value to be enqueued. 74 """ 75 self.array.append(value)
Enqueue the element inside the array.
Arguments:
- value (any): Value to be enqueued.
77 def dequeue(self): # O(n) 78 """Dequeue the element from the array. 79 80 Since this operation is using `.pop(0)`, it has O(n) time complexity because 81 all the values in the array is shifted. 82 83 Args: 84 value (any): Value to be dequeued. 85 """ 86 return self.array.pop(0)
Dequeue the element from the array.
Since this operation is using .pop(0), it has O(n) time complexity because
all the values in the array is shifted.
Arguments:
- value (any): Value to be dequeued.
88 def is_empty(self): 89 """bool: Return True if the size is 0, else False.""" 90 return self.size == 0
bool: Return True if the size is 0, else False.
101class LinkedQueue(LinkedList): 102 """A queue that's using LinkedList object.""" 103 104 def __init__(self, *values): 105 """Initialize a queue based on linked list data structure. 106 107 Args: 108 *values (List): Variable length argument list. Those are the elements to be stored in the linked queue. 109 """ 110 super().__init__() 111 for value in values: 112 self.insert_tail(value) 113 114 @property 115 def front(self): 116 """any: The value at front if the linked queue is not empty. Otherwise None.""" 117 return self.head_node.value if not self.is_empty() else None 118 119 @property 120 def rear(self): 121 """any: The value at rear if the linked queue is not empty. Otherwise None.""" 122 return self.tail_node.value if not self.is_empty() else None 123 124 def enqueue(self, value): # O(1) 125 """Enqueue an element inside the linked queue. 126 127 This method uses the `insert_tail(value)` method from the LinkedList. 128 129 Args: 130 value (any): Value to insert. 131 """ 132 self.insert_tail(value) 133 134 def dequeue(self): # O(1) 135 """Dequeue an element from the linked queue. 136 137 This method uses the `insert_tail(value)` method from the LinkedList. 138 139 Raises: 140 Exception: If the list is empty. 141 """ 142 return self.delete_head() 143 144 def is_empty(self): 145 """bool: Return True if the size is 0, else False.""" 146 return self.size == 0 147 148 def clear(self): 149 """Clears the whole array of the LinkedQueue. 150 151 Raises: 152 Exception: If the list is empty. 153 """ 154 while not self.is_empty(): 155 self.delete_head() 156 157 def copy(self): 158 """Returns a deep copy of the LinkedQueue object with the same values.""" 159 return LinkedQueue(*self)
A queue that's using LinkedList object.
104 def __init__(self, *values): 105 """Initialize a queue based on linked list data structure. 106 107 Args: 108 *values (List): Variable length argument list. Those are the elements to be stored in the linked queue. 109 """ 110 super().__init__() 111 for value in values: 112 self.insert_tail(value)
Initialize a queue based on linked list data structure.
Arguments:
- *values (List): Variable length argument list. Those are the elements to be stored in the linked queue.
114 @property 115 def front(self): 116 """any: The value at front if the linked queue is not empty. Otherwise None.""" 117 return self.head_node.value if not self.is_empty() else None
any: The value at front if the linked queue is not empty. Otherwise None.
119 @property 120 def rear(self): 121 """any: The value at rear if the linked queue is not empty. Otherwise None.""" 122 return self.tail_node.value if not self.is_empty() else None
any: The value at rear if the linked queue is not empty. Otherwise None.
124 def enqueue(self, value): # O(1) 125 """Enqueue an element inside the linked queue. 126 127 This method uses the `insert_tail(value)` method from the LinkedList. 128 129 Args: 130 value (any): Value to insert. 131 """ 132 self.insert_tail(value)
Enqueue an element inside the linked queue.
This method uses the insert_tail(value) method from the LinkedList.
Arguments:
- value (any): Value to insert.
134 def dequeue(self): # O(1) 135 """Dequeue an element from the linked queue. 136 137 This method uses the `insert_tail(value)` method from the LinkedList. 138 139 Raises: 140 Exception: If the list is empty. 141 """ 142 return self.delete_head()
Dequeue an element from the linked queue.
This method uses the insert_tail(value) method from the LinkedList.
Raises:
- Exception: If the list is empty.
144 def is_empty(self): 145 """bool: Return True if the size is 0, else False.""" 146 return self.size == 0
bool: Return True if the size is 0, else False.
162class CircularQueue: 163 """Circular queue implementation to handle empty space problem at the front. 164 165 Attributes: 166 capacity (int): Capacity of the circular queue. Default size is 50. 167 array (List[any]): The list to be behaved as a circular queue. 168 front_idx (int): Index of the front value of the array. -1 if the array is empty. 169 rear_idx (int): Index of the rear value of the array. -1 if the array is empty. 170 size (int): Size of the circular array. 171 """ 172 173 def __init__(self, *values, capacity=50): 174 """Initialize a CircularQueue object. 175 176 Args: 177 capacity (int): Capacity of the circular queue. Default size is 50. 178 array (List[any]): The list to be behaved as a circular queue. 179 front_idx (int): Index of the front value of the array. -1 if the array is empty. 180 rear_idx (int): Index of the rear value of the array. -1 if the array is empty. 181 size (int): Size of the circular array. 182 *values (List): Variable length argument list. Those are the elements to be stored in the linked queue. 183 """ 184 self.capacity = capacity 185 self.array = [None] * self.capacity 186 self.front_idx = -1 187 self.rear_idx = -1 188 self._size = 0 189 for value in values: 190 self.enqueue(value) 191 192 @property 193 def size(self): 194 """int: Size of the array.""" 195 return self._size 196 197 @property 198 def front(self): 199 """any: The value at front if the circular queue is not empty. Otherwise None.""" 200 return self.array[self.front_idx] if not self.is_empty() else None 201 202 @property 203 def rear(self): 204 """any: The value at rear if the circular queue is not empty. Otherwise None.""" 205 return self.array[self.rear_idx] if not self.is_empty() else None 206 207 def __str__(self): 208 """str: String representation of the CircularQueue object.""" 209 string = "[ " 210 idx = self.front_idx 211 for _ in range(self.size): 212 string += str(self.array[idx]) + " " 213 idx = (idx + 1) % self.capacity 214 return string + "]" 215 216 def __repr__(self): 217 """str: Representation of CircularQueue object for debugging.""" 218 return ( 219 f"CircularQueue(array={self.array}, " 220 f"capacity={self.capacity}, " 221 f"front_idx={self.front_idx}, " 222 f"rear_idx={self.rear_idx}, " 223 f"size={self.size})" 224 ) 225 226 def __len__(self): 227 """int: Size of the array.""" 228 return self._size 229 230 def __getitem__(self, index): 231 """Make CircularQueue class indexable. 232 233 Args: 234 index (int): Index of `CircularQueue` object to return the value at that position. 235 236 Raises: 237 IndexError: If the `index` is out of range. 238 """ 239 if index < 0: 240 index += self.size 241 if not 0 <= index < self.size: 242 raise IndexError(f"Index {index} is out of bounds") 243 else: 244 physical_index = (self.front_idx + index) % self.capacity 245 return self.array[physical_index] 246 247 def __iter__(self): 248 """Dunder method to make `CircularQueue` iterable.""" 249 for i in range(self.size): 250 physical_index = (self.front_idx + i) % self.capacity 251 yield self.array[physical_index] 252 253 def enqueue(self, value): 254 """Enqueue data for a circular queue. 255 256 This is a manual implementation instead of using %. Each time a new insertion is 257 made, the front_idx and rear_idx changes. 258 259 Args: 260 value (any): To enqueue inside the CircularQueue. 261 262 Raises: 263 Exception: If the CircularQueue is full. 264 """ 265 if self.is_full(): 266 raise Exception("The circular queue is full") 267 elif self.is_empty(): 268 self.front_idx = self.rear_idx = 0 269 elif self.is_rear_idx_reached() and self.front_idx != 0: 270 self.rear_idx = 0 271 else: 272 self.rear_idx = self.rear_idx + 1 273 self.array[self.rear_idx] = value 274 self._size += 1 275 276 def dequeue(self): 277 """Dequeue an element from the CircularQueue. 278 279 Using the modular arithmetics, front and rear indices are updated. 280 281 Raises: 282 Exception: If the circular queue is empty. 283 """ 284 if self.is_empty(): 285 raise Exception("The circular queue is empty") 286 value = self.array[self.front_idx] 287 self.array[self.front_idx] = None 288 self.front_idx = (self.front_idx + 1) % self.capacity 289 self._size -= 1 290 if self._size == 0: 291 self.front_idx = -1 292 self.rear_idx = -1 293 return value 294 295 def is_empty(self): 296 """bool: Return True if the size is 0, else False.""" 297 return self.size == 0 298 299 def is_full(self): 300 """bool: Return True if the queue is full capacity, else False.""" 301 return (self.rear_idx + 1) % self.capacity == self.front_idx 302 303 def is_rear_idx_reached(self): 304 """bool: Return True if the `rear_idx` reached at the end, else False. 305 306 This is used to check if the rear_idx should be jumped to the front of the circular queue. 307 """ 308 return self.rear_idx == self.capacity - 1 309 310 def clear(self): 311 """Clears the whole CircularQueue object. 312 313 Creates the default array representation. Reset the front and rear indices, set the size to 0. 314 """ 315 self.array = [None] * self.capacity 316 self.front_idx = -1 317 self.rear_idx = -1 318 self._size = 0 319 320 def copy(self): 321 """Return a deep copy of the CircularQueue object.""" 322 return CircularQueue(*self, capacity=self.capacity)
Circular queue implementation to handle empty space problem at the front.
Attributes:
- capacity (int): Capacity of the circular queue. Default size is 50.
- array (List[any]): The list to be behaved as a circular queue.
- front_idx (int): Index of the front value of the array. -1 if the array is empty.
- rear_idx (int): Index of the rear value of the array. -1 if the array is empty.
- size (int): Size of the circular array.
173 def __init__(self, *values, capacity=50): 174 """Initialize a CircularQueue object. 175 176 Args: 177 capacity (int): Capacity of the circular queue. Default size is 50. 178 array (List[any]): The list to be behaved as a circular queue. 179 front_idx (int): Index of the front value of the array. -1 if the array is empty. 180 rear_idx (int): Index of the rear value of the array. -1 if the array is empty. 181 size (int): Size of the circular array. 182 *values (List): Variable length argument list. Those are the elements to be stored in the linked queue. 183 """ 184 self.capacity = capacity 185 self.array = [None] * self.capacity 186 self.front_idx = -1 187 self.rear_idx = -1 188 self._size = 0 189 for value in values: 190 self.enqueue(value)
Initialize a CircularQueue object.
Arguments:
- capacity (int): Capacity of the circular queue. Default size is 50.
- array (List[any]): The list to be behaved as a circular queue.
- front_idx (int): Index of the front value of the array. -1 if the array is empty.
- rear_idx (int): Index of the rear value of the array. -1 if the array is empty.
- size (int): Size of the circular array.
- *values (List): Variable length argument list. Those are the elements to be stored in the linked queue.
197 @property 198 def front(self): 199 """any: The value at front if the circular queue is not empty. Otherwise None.""" 200 return self.array[self.front_idx] if not self.is_empty() else None
any: The value at front if the circular queue is not empty. Otherwise None.
202 @property 203 def rear(self): 204 """any: The value at rear if the circular queue is not empty. Otherwise None.""" 205 return self.array[self.rear_idx] if not self.is_empty() else None
any: The value at rear if the circular queue is not empty. Otherwise None.
253 def enqueue(self, value): 254 """Enqueue data for a circular queue. 255 256 This is a manual implementation instead of using %. Each time a new insertion is 257 made, the front_idx and rear_idx changes. 258 259 Args: 260 value (any): To enqueue inside the CircularQueue. 261 262 Raises: 263 Exception: If the CircularQueue is full. 264 """ 265 if self.is_full(): 266 raise Exception("The circular queue is full") 267 elif self.is_empty(): 268 self.front_idx = self.rear_idx = 0 269 elif self.is_rear_idx_reached() and self.front_idx != 0: 270 self.rear_idx = 0 271 else: 272 self.rear_idx = self.rear_idx + 1 273 self.array[self.rear_idx] = value 274 self._size += 1
Enqueue data for a circular queue.
This is a manual implementation instead of using %. Each time a new insertion is made, the front_idx and rear_idx changes.
Arguments:
- value (any): To enqueue inside the CircularQueue.
Raises:
- Exception: If the CircularQueue is full.
276 def dequeue(self): 277 """Dequeue an element from the CircularQueue. 278 279 Using the modular arithmetics, front and rear indices are updated. 280 281 Raises: 282 Exception: If the circular queue is empty. 283 """ 284 if self.is_empty(): 285 raise Exception("The circular queue is empty") 286 value = self.array[self.front_idx] 287 self.array[self.front_idx] = None 288 self.front_idx = (self.front_idx + 1) % self.capacity 289 self._size -= 1 290 if self._size == 0: 291 self.front_idx = -1 292 self.rear_idx = -1 293 return value
Dequeue an element from the CircularQueue.
Using the modular arithmetics, front and rear indices are updated.
Raises:
- Exception: If the circular queue is empty.
295 def is_empty(self): 296 """bool: Return True if the size is 0, else False.""" 297 return self.size == 0
bool: Return True if the size is 0, else False.
299 def is_full(self): 300 """bool: Return True if the queue is full capacity, else False.""" 301 return (self.rear_idx + 1) % self.capacity == self.front_idx
bool: Return True if the queue is full capacity, else False.
303 def is_rear_idx_reached(self): 304 """bool: Return True if the `rear_idx` reached at the end, else False. 305 306 This is used to check if the rear_idx should be jumped to the front of the circular queue. 307 """ 308 return self.rear_idx == self.capacity - 1
bool: Return True if the rear_idx reached at the end, else False.
This is used to check if the rear_idx should be jumped to the front of the circular queue.
310 def clear(self): 311 """Clears the whole CircularQueue object. 312 313 Creates the default array representation. Reset the front and rear indices, set the size to 0. 314 """ 315 self.array = [None] * self.capacity 316 self.front_idx = -1 317 self.rear_idx = -1 318 self._size = 0
Clears the whole CircularQueue object.
Creates the default array representation. Reset the front and rear indices, set the size to 0.
325class PriorityQueue(LinkedList): 326 """Implementation of priority queue, where queue is made up of linked lists with priority levels. 327 328 Attributes: 329 value (Any): Value of the node. 330 """ 331 332 def __init__(self, value=None): 333 """Initialize a PriorityQueue object. 334 335 This is used a specialized node class called `NodePriorityQueue`. 336 337 Args: 338 value (Any): Value of the node. 339 """ 340 super().__init__(value, node=NodePriorityQueue) 341 342 @property 343 def front(self): 344 """Return the value at the front of the queue, or None if empty.""" 345 return self.head_node.value if not self.is_empty() else None 346 347 @property 348 def rear(self): 349 """any: The value at front (tail node's value of the linked list) if the queue is not empty. Otherwise None.""" 350 return self.tail_node.value if not self.is_empty() else None 351 352 def enqueue(self, value, priority): 353 """Enqueue the element inside the linked queue. 354 355 First, a value is wrapped inside the node object. If there's a node with a higher priority, 356 it's inserted to front. The nodes with the same priority level are behaved like a regular queue (FIFO). 357 358 Args: 359 value (any): Value to be enqueued. 360 priority (int): Priority level 361 """ 362 new_node = self._Node(value, priority) 363 current_node = self.head_node 364 prev_node = None 365 366 while current_node is not None: 367 if new_node.priority > current_node.priority: 368 if prev_node is None: 369 self._insert_node_head(new_node) 370 return 371 else: 372 prev_node.next_node = new_node 373 new_node.next_node = current_node 374 self._size += 1 375 return 376 elif new_node.priority == current_node.priority: 377 new_node.next_node = current_node.next_node 378 current_node.next_node = new_node 379 self._size += 1 380 return 381 prev_node = current_node 382 current_node = current_node.next_node 383 384 if prev_node is not None: 385 prev_node.next_node = new_node 386 self.tail_node = new_node 387 else: 388 self.head_node = new_node 389 self._size += 1 390 391 def dequeue(self): 392 """Dequeue the element (remove the head node from the list). 393 394 Raises: 395 Exception: If the priority queue is empty. 396 """ 397 if self.size == 0: 398 raise Exception("The priority queue is empty") 399 value = self.head_node.value 400 self.delete_head() 401 return value 402 403 def _insert_node_head(self, new_node): 404 new_node.next_node = self.head_node 405 self.head_node = new_node 406 self._size += 1 407 if self._size == 1: 408 self.tail_node = new_node 409 410 def is_empty(self): 411 """bool: Return True if the size is 0, else False.""" 412 return self.size == 0 413 414 def clear(self): 415 """Clear the whole PriorityQueue object.""" 416 while not self.is_empty(): 417 self.delete_head() 418 419 def copy(self): 420 """Return a deep copy of the current PriorityQueue object.""" 421 new_queue = PriorityQueue() 422 current_node = self.head_node 423 while current_node is not None: 424 new_queue.enqueue(current_node.value, current_node.priority) 425 current_node = current_node.next_node 426 return new_queue
Implementation of priority queue, where queue is made up of linked lists with priority levels.
Attributes:
- value (Any): Value of the node.
332 def __init__(self, value=None): 333 """Initialize a PriorityQueue object. 334 335 This is used a specialized node class called `NodePriorityQueue`. 336 337 Args: 338 value (Any): Value of the node. 339 """ 340 super().__init__(value, node=NodePriorityQueue)
Initialize a PriorityQueue object.
This is used a specialized node class called NodePriorityQueue.
Arguments:
- value (Any): Value of the node.
342 @property 343 def front(self): 344 """Return the value at the front of the queue, or None if empty.""" 345 return self.head_node.value if not self.is_empty() else None
Return the value at the front of the queue, or None if empty.
347 @property 348 def rear(self): 349 """any: The value at front (tail node's value of the linked list) if the queue is not empty. Otherwise None.""" 350 return self.tail_node.value if not self.is_empty() else None
any: The value at front (tail node's value of the linked list) if the queue is not empty. Otherwise None.
352 def enqueue(self, value, priority): 353 """Enqueue the element inside the linked queue. 354 355 First, a value is wrapped inside the node object. If there's a node with a higher priority, 356 it's inserted to front. The nodes with the same priority level are behaved like a regular queue (FIFO). 357 358 Args: 359 value (any): Value to be enqueued. 360 priority (int): Priority level 361 """ 362 new_node = self._Node(value, priority) 363 current_node = self.head_node 364 prev_node = None 365 366 while current_node is not None: 367 if new_node.priority > current_node.priority: 368 if prev_node is None: 369 self._insert_node_head(new_node) 370 return 371 else: 372 prev_node.next_node = new_node 373 new_node.next_node = current_node 374 self._size += 1 375 return 376 elif new_node.priority == current_node.priority: 377 new_node.next_node = current_node.next_node 378 current_node.next_node = new_node 379 self._size += 1 380 return 381 prev_node = current_node 382 current_node = current_node.next_node 383 384 if prev_node is not None: 385 prev_node.next_node = new_node 386 self.tail_node = new_node 387 else: 388 self.head_node = new_node 389 self._size += 1
Enqueue the element inside the linked queue.
First, a value is wrapped inside the node object. If there's a node with a higher priority, it's inserted to front. The nodes with the same priority level are behaved like a regular queue (FIFO).
Arguments:
- value (any): Value to be enqueued.
- priority (int): Priority level
391 def dequeue(self): 392 """Dequeue the element (remove the head node from the list). 393 394 Raises: 395 Exception: If the priority queue is empty. 396 """ 397 if self.size == 0: 398 raise Exception("The priority queue is empty") 399 value = self.head_node.value 400 self.delete_head() 401 return value
Dequeue the element (remove the head node from the list).
Raises:
- Exception: If the priority queue is empty.
410 def is_empty(self): 411 """bool: Return True if the size is 0, else False.""" 412 return self.size == 0
bool: Return True if the size is 0, else False.
414 def clear(self): 415 """Clear the whole PriorityQueue object.""" 416 while not self.is_empty(): 417 self.delete_head()
Clear the whole PriorityQueue object.
419 def copy(self): 420 """Return a deep copy of the current PriorityQueue object.""" 421 new_queue = PriorityQueue() 422 current_node = self.head_node 423 while current_node is not None: 424 new_queue.enqueue(current_node.value, current_node.priority) 425 current_node = current_node.next_node 426 return new_queue
Return a deep copy of the current PriorityQueue object.
7class StackArray: 8 """Stack data structure using an array. 9 10 Attributes: 11 array (List): Empty array. This will contain the data. 12 """ 13 14 def __init__(self, *args): 15 """Initialize a `StackArray` object. 16 17 Args: 18 *args: Variable length argument list. Those are basically the elements to be stored. 19 """ 20 self.array = [] 21 for arg in args: 22 self.push(arg) 23 24 @property 25 def size(self): 26 """int: Size of the array.""" 27 return len(self.array) 28 29 @property 30 def top(self): 31 """any: The value at the top. Otherwise None.""" 32 return self.array[-1] if not self.is_empty() else None 33 34 def __str__(self): 35 """str: String representation of `StackArray` object.""" 36 return "[ " + " ".join(str(element) for element in reversed(self.array)) + " ]" 37 38 def __len__(self): 39 """int: Return the size of the stack.""" 40 return self.size 41 42 def __iter__(self): 43 """Dunder method to make `StackArray` object iterable.""" 44 return reversed(self.array) 45 46 def __contains__(self, value): 47 """Dunder method to have a more pythonic experience. 48 49 Args: 50 value (any): The value to be pushed into the stack. 51 52 Example: 53 >>> stack = StackArray(1, 2, 3) 54 >>> if 3 in stack: 55 >>> print("3 is inside the stack") 56 """ 57 return value in self.array 58 59 def push(self, value): 60 """Push an element into the stack. 61 62 Args: 63 value (any): Value to be inserted. 64 """ 65 self.array.append(value) 66 67 def pop(self): 68 """Pop an element from the stack. 69 70 Raises: 71 IndexError: If the stack is empty. 72 """ 73 if self.size == 0: 74 raise IndexError("The stack is empty") 75 else: 76 value = self.array.pop() 77 return value 78 79 def peek(self): 80 """Return the top element without popping the element. 81 82 Raises: 83 IndexError: If the stack is empty. 84 """ 85 if self.size == 0: 86 raise IndexError("The stack is empty") 87 else: 88 return self.top 89 90 def is_empty(self): 91 """bool: Return True if the size is 0. Otherwise False.""" 92 return self.size == 0 93 94 def clear(self): 95 """Clear the whole `StackArray` object.""" 96 self.array.clear() 97 98 def copy(self): 99 """Return a deep copy of the `StackArray` object. 100 101 The `*self.array` unpacks elements into a new stack. 102 """ 103 return StackArray(*self.array)
Stack data structure using an array.
Attributes:
- array (List): Empty array. This will contain the data.
14 def __init__(self, *args): 15 """Initialize a `StackArray` object. 16 17 Args: 18 *args: Variable length argument list. Those are basically the elements to be stored. 19 """ 20 self.array = [] 21 for arg in args: 22 self.push(arg)
Initialize a StackArray object.
Arguments:
- *args: Variable length argument list. Those are basically the elements to be stored.
29 @property 30 def top(self): 31 """any: The value at the top. Otherwise None.""" 32 return self.array[-1] if not self.is_empty() else None
any: The value at the top. Otherwise None.
59 def push(self, value): 60 """Push an element into the stack. 61 62 Args: 63 value (any): Value to be inserted. 64 """ 65 self.array.append(value)
Push an element into the stack.
Arguments:
- value (any): Value to be inserted.
67 def pop(self): 68 """Pop an element from the stack. 69 70 Raises: 71 IndexError: If the stack is empty. 72 """ 73 if self.size == 0: 74 raise IndexError("The stack is empty") 75 else: 76 value = self.array.pop() 77 return value
Pop an element from the stack.
Raises:
- IndexError: If the stack is empty.
79 def peek(self): 80 """Return the top element without popping the element. 81 82 Raises: 83 IndexError: If the stack is empty. 84 """ 85 if self.size == 0: 86 raise IndexError("The stack is empty") 87 else: 88 return self.top
Return the top element without popping the element.
Raises:
- IndexError: If the stack is empty.
90 def is_empty(self): 91 """bool: Return True if the size is 0. Otherwise False.""" 92 return self.size == 0
bool: Return True if the size is 0. Otherwise False.
106class LinkedStack(LinkedList): 107 """This is the stack implementation using linked lists. It has all the functionality a `LinkedList` has.""" 108 109 def __init__(self, *values): 110 """Initialize a `LinkedStack` object. 111 112 Args: 113 *values: Variable length argument list. Those are basically the elements to be stored. 114 """ 115 super().__init__() 116 for value in values: 117 self.insert_head(value) 118 119 @property 120 def top(self): 121 """any: The value at the top. Otherwise None.""" 122 return self.head_node.value if self.head_node else None 123 124 def push(self, value): 125 """Push an element into the stack. 126 127 Since it's using a linked list, this method is inserting the value to the head of the list. 128 129 Args: 130 value (any): Value to be inserted. 131 """ 132 self.insert_head(value) 133 134 def pop(self): 135 """Pop an element from the stack. 136 137 Raises: 138 Exception: If the stack is empty. 139 """ 140 if self.size > 0: 141 element = self.head_node.value 142 self.delete_value(self.head_node.value) 143 return element 144 else: 145 raise Exception("The stack list is empty") 146 147 def peek(self): 148 """Return the top element without popping the element. 149 150 Raises: 151 IndexError: If the stack is empty. 152 """ 153 if self.size == 0: 154 raise IndexError("The stack is empty") 155 else: 156 return self.top 157 158 def is_empty(self): 159 """bool: Return True if the size is 0. Otherwise False.""" 160 return self.size == 0 161 162 def clear(self): 163 """Clear the whole `LinkedArray` object.""" 164 while self.size != 0: 165 self.delete_head() 166 167 def copy(self): 168 """Return a deep copy of the `LinkedStack` object.""" 169 return LinkedStack(*reversed(list(self)))
This is the stack implementation using linked lists. It has all the functionality a LinkedList has.
109 def __init__(self, *values): 110 """Initialize a `LinkedStack` object. 111 112 Args: 113 *values: Variable length argument list. Those are basically the elements to be stored. 114 """ 115 super().__init__() 116 for value in values: 117 self.insert_head(value)
Initialize a LinkedStack object.
Arguments:
- *values: Variable length argument list. Those are basically the elements to be stored.
119 @property 120 def top(self): 121 """any: The value at the top. Otherwise None.""" 122 return self.head_node.value if self.head_node else None
any: The value at the top. Otherwise None.
124 def push(self, value): 125 """Push an element into the stack. 126 127 Since it's using a linked list, this method is inserting the value to the head of the list. 128 129 Args: 130 value (any): Value to be inserted. 131 """ 132 self.insert_head(value)
Push an element into the stack.
Since it's using a linked list, this method is inserting the value to the head of the list.
Arguments:
- value (any): Value to be inserted.
134 def pop(self): 135 """Pop an element from the stack. 136 137 Raises: 138 Exception: If the stack is empty. 139 """ 140 if self.size > 0: 141 element = self.head_node.value 142 self.delete_value(self.head_node.value) 143 return element 144 else: 145 raise Exception("The stack list is empty")
Pop an element from the stack.
Raises:
- Exception: If the stack is empty.
147 def peek(self): 148 """Return the top element without popping the element. 149 150 Raises: 151 IndexError: If the stack is empty. 152 """ 153 if self.size == 0: 154 raise IndexError("The stack is empty") 155 else: 156 return self.top
Return the top element without popping the element.
Raises:
- IndexError: If the stack is empty.
158 def is_empty(self): 159 """bool: Return True if the size is 0. Otherwise False.""" 160 return self.size == 0
bool: Return True if the size is 0. Otherwise False.
162 def clear(self): 163 """Clear the whole `LinkedArray` object.""" 164 while self.size != 0: 165 self.delete_head()
Clear the whole LinkedArray object.
167 def copy(self): 168 """Return a deep copy of the `LinkedStack` object.""" 169 return LinkedStack(*reversed(list(self)))
Return a deep copy of the LinkedStack object.
10class BinarySearchTree: 11 def __init__(self, value=None, node=TreeNode): 12 self._Node = node 13 if value is None: 14 self._root = None 15 self._size = 0 16 else: 17 self._root = self._Node(value) 18 self._size = 1 19 20 @property 21 def root(self): 22 return self._root 23 24 @root.setter 25 def root(self, value): 26 self._root = value 27 28 @property 29 def min(self): 30 return self.find_min().value 31 32 @property 33 def max(self): 34 return self.find_max().value 35 36 @property 37 def size(self): 38 return self._size 39 40 @property 41 def is_empty(self): 42 return self.root is None 43 44 def __len__(self): 45 return self._size 46 47 def __iter__(self): 48 for node in self._inorder(self.root): 49 yield node 50 51 def __contains__(self, value): 52 node = self.search_element(value) 53 if node is not None: 54 return True 55 else: 56 return False 57 58 def _check_empty(self): 59 if self.root is None: 60 raise Exception("Binary search tree is empty") 61 62 def _check_param_type(self, param_type, keyword_list): 63 if not isinstance(param_type, str): 64 raise TypeError( 65 f"Invalid type: {type(param_type)} for argument. Enter relevant 'str'" 66 + "keywords from the list {keyword_list}" 67 ) 68 elif param_type not in keyword_list: 69 raise Exception( 70 f"Invalid keyword. Choose one in between the list: {keyword_list}" 71 ) 72 73 def inorder_traversal(self, output_type=default_keyword): 74 self._check_param_type(output_type, traversal_keywords) 75 self._check_empty() 76 77 if output_type == "list": 78 return list(node.value for node in self._inorder(self.root)) 79 elif output_type == "print": 80 for i in self._inorder(self.root): 81 print(i.value) 82 elif output_type == "generator": 83 return self._inorder(self.root) 84 85 def _inorder(self, node): 86 if node is None: 87 return 88 yield from self._inorder(node.left_child) 89 yield node 90 yield from self._inorder(node.right_child) 91 92 def preorder_traversal(self, output_type=default_keyword): 93 self._check_param_type(output_type, traversal_keywords) 94 self._check_empty() 95 96 if output_type == "list": 97 return list(node.value for node in self._preorder(self.root)) 98 elif output_type == "print": 99 for i in self._preorder(self.root): 100 print(i.value) 101 elif output_type == "generator": 102 return self._preorder(self.root) 103 104 def _preorder(self, node): 105 if node is None: 106 return 107 yield node 108 yield from self._preorder(node.left_child) 109 yield from self._preorder(node.right_child) 110 111 def postorder_traversal(self, output_type=default_keyword): 112 self._check_param_type(output_type, traversal_keywords) 113 self._check_empty() 114 115 if output_type == "list": 116 return list(node.value for node in self._postorder(self.root)) 117 elif output_type == "print": 118 for i in self._postorder(self.root): 119 print(i.value) 120 elif output_type == "generator": 121 return self._postorder(self.root) 122 123 def _postorder(self, node): 124 if node is None: 125 return 126 yield from self._postorder(node.left_child) 127 yield from self._postorder(node.right_child) 128 yield node 129 130 def levelorder_traversal(self, none_value=None): 131 if self.root is none_value: 132 return none_value 133 queue = QueueArray(self.root) 134 result = [] 135 while queue.size != 0: 136 current_node = queue.dequeue() 137 result.append(current_node.value) 138 if current_node.left_child is not none_value: 139 queue.enqueue(current_node.left_child) 140 if current_node.right_child is not none_value: 141 queue.enqueue(current_node.right_child) 142 return result 143 144 def insert(self, data, node=None): 145 if not isinstance(data, self._Node): 146 new_node = self._Node(data) 147 else: 148 new_node = data 149 150 if self._root is None: 151 self.root = new_node 152 self._size += 1 153 return new_node 154 else: 155 current_node = self.root if node is None else node 156 if new_node.value >= current_node.value: 157 if current_node.right_child is None: 158 current_node.right_child = new_node 159 new_node.parent = current_node 160 self._size += 1 161 return new_node 162 else: 163 return self.insert(new_node, current_node.right_child) 164 else: 165 if current_node.left_child is None: 166 current_node.left_child = new_node 167 new_node.parent = current_node 168 self._size += 1 169 return new_node 170 else: 171 return self.insert(new_node, current_node.left_child) 172 173 def insert_iterative(self, data): 174 if not isinstance(data, self._Node): 175 new_node = self._Node(data) 176 else: 177 new_node = data 178 179 if self.root is None: 180 self.root = new_node 181 self._size += 1 182 return new_node 183 else: 184 current_node = self.root 185 while True: 186 if new_node.value >= current_node.value: 187 if current_node.right_child is None: 188 current_node.right_child = new_node 189 new_node.parent = current_node 190 self._size += 1 191 return new_node 192 current_node = current_node.right_child 193 elif new_node.value < current_node.value: 194 if current_node.left_child is None: 195 current_node.left_child = new_node 196 new_node.parent = current_node 197 self._size += 1 198 return new_node 199 current_node = current_node.left_child 200 201 def search_element(self, value, node=None): 202 if not isinstance(value, self._Node): 203 searched_node = self._Node(value) 204 else: 205 searched_node = value 206 current_node = self.root if node is None else node 207 208 if self._root is None: 209 raise Exception("Binary search tree is empty") 210 elif current_node.value == searched_node.value: 211 return current_node 212 else: 213 if searched_node.value < current_node.value: 214 if current_node.left_child is None: 215 return None 216 return self.search_element(searched_node, current_node.left_child) 217 else: 218 if current_node.right_child is None: 219 return None 220 return self.search_element(searched_node, current_node.right_child) 221 222 def search_iterative(self, value, none_value=None): 223 if self._root is none_value: 224 raise Exception("Binary search tree is empty") 225 current_node = self.root 226 while current_node is not none_value: 227 if current_node.value == value: 228 return current_node 229 elif current_node.value >= value: 230 current_node = current_node.left_child 231 else: 232 current_node = current_node.right_child 233 raise Exception(f"Value {value} is not found") 234 235 def delete_node(self, value, node=None): 236 if node is None: 237 node = self.search_element(value, node=node) # returns None if element couldn't be found 238 if node is None: 239 raise Exception(f"{value} does not exist in the tree") 240 241 parent_node = node.parent 242 # deleting a node that has no children 243 if node.left_child is None and node.right_child is None: 244 if parent_node.left_child == node: 245 parent_node.left_child = None 246 elif parent_node.right_child == node: 247 parent_node.right_child = None 248 self._size -= 1 249 # deleting a node that has only one child 250 elif node.left_child is None and node.right_child is not None: 251 if parent_node.left_child == node: 252 parent_node.left_child = node.right_child 253 else: 254 parent_node.right_child = node.right_child 255 self._size -= 1 256 elif node.left_child is not None and node.right_child is None: 257 if parent_node.left_child == node: 258 parent_node.left_child = node.left_child 259 else: 260 parent_node.right_child = node.left_child 261 self._size -= 1 262 # deleting a node with two children (using in order successor) 263 else: 264 in_order_successor = node.right_child 265 while in_order_successor.left_child: 266 in_order_successor = in_order_successor.left_child 267 node.value = in_order_successor.value 268 # now there are two nodes with the same value. when self.delete() called, \ 269 # as a default value None, the search_element() method will start looking \ 270 # by the root node. every time this will end up in the first occurence of the \ 271 # same value. to avoid this, we can start from node.right_child but we already \ 272 # have access to the real in_order_successor, so the function below in fact \ 273 # will take O(1) time. 274 self.delete_node(in_order_successor.value, in_order_successor) 275 276 def delete_iterative(self, value): 277 node = self._root 278 parent_node = None 279 280 while node is not None and node.value != value: 281 parent_node = node 282 if node.value > value: 283 node = node.left_child 284 else: 285 node = node.right_child 286 287 if node is None: 288 raise Exception(f"{value} is not found in the tree") 289 290 # deleting a node that has no children 291 if node.left_child is None and node.right_child is None: 292 if parent_node is None: 293 self.root = None 294 elif parent_node.left_child == node: 295 parent_node.left_child = None 296 elif parent_node.right_child == node: 297 parent_node.right_child = None 298 self._size -= 1 299 # deleting a node that has only one child 300 elif node.left_child is None or node.right_child is None: 301 child_node = node.left_child if node.left_child else node.right_child 302 if parent_node is None: 303 self.root = child_node 304 elif parent_node.left_child == node: 305 parent_node.left_child = child_node 306 else: 307 parent_node.right_child = child_node 308 if child_node: 309 child_node.parent = parent_node 310 # deleting a node with two children (using in order successor) 311 else: 312 successor_parent = node 313 successor = node.right_child 314 while successor.left_child is not None: 315 successor_parent = successor 316 successor = successor.left_child 317 318 node.value = successor.value 319 if successor_parent.left_child == successor: 320 successor_parent.left_child = successor.right_child 321 else: 322 successor_parent.right_child = successor.right_child 323 324 if successor.right_child: # Update parent if successor had a right child 325 successor.right_child.parent = successor_parent 326 self._size -= 1 327 328 def find_min(self, node=None, return_value=False, none_value=None): 329 self._check_empty() 330 if node is none_value: 331 current_node = self.root 332 else: 333 current_node = node 334 335 while current_node.left_child is not none_value: 336 current_node = current_node.left_child 337 338 if return_value is True: 339 return current_node.value 340 return current_node 341 342 def find_max(self, node=None, return_value=False, none_value=None): 343 self._check_empty() 344 if node is none_value: 345 current_node = self.root 346 else: 347 current_node = node 348 349 while current_node.right_child is not none_value: 350 current_node = current_node.right_child 351 if return_value is True: 352 return current_node.value 353 return current_node 354 355 def height(self, opt_node=_sentinel): 356 if opt_node is _sentinel: 357 return self.height(self.root) 358 if opt_node is None: 359 return 0 360 else: 361 left_height = self.height(opt_node.left_child) 362 right_height = self.height(opt_node.right_child) 363 return max(left_height, right_height) + 1 364 365 def get_size(self): 366 if self.root is None: 367 return 0 368 369 def _size(node=None): 370 if node is None: 371 return 0 372 return _size(node.left_child) + _size(node.right_child) + 1 373 374 ### to be removed later 375 if _size(self.root) != self._size: 376 print("get_size() fonksiyonundan dönüldü. self_size'da hata olmalı") 377 return 378 ### 379 return _size(self.root) 380 381 def clear(self): 382 self.root = None 383 self._size = 0 384 385 def copy(self, method="recursive"): 386 self._check_empty() 387 self._check_param_type(method, copy_keywords) 388 tree = BinarySearchTree() 389 390 def _copy_iterative(): 391 for node in self._preorder(self.root): 392 tree.insert(node.value) 393 return tree 394 395 def _copy_recursive(node, parent): 396 if node is None: 397 return None 398 new_node = self._Node(node.value) 399 new_node.parent = parent 400 tree._size += 1 401 new_node.left_child = _copy_recursive(node.left_child, new_node) 402 new_node.right_child = _copy_recursive(node.right_child, new_node) 403 return new_node 404 405 if method == "iterative": 406 return _copy_iterative() 407 else: 408 tree.root = _copy_recursive(self.root, self.root.parent) 409 return tree 410 411 def is_balanced(self, node=None): 412 if node is None: 413 node = self.root 414 if not isinstance(node, TreeNode): 415 raise Exception(f"Please provide a {TreeNode} object") 416 left_subtree_height = self.height(node.left_child) 417 right_subtree_height = self.height(node.right_child) 418 return False if abs(left_subtree_height - right_subtree_height) > 1 else True 419 420 def pretty_print(self, none_value=None): 421 """Prints the tree in a visually appealing way in the terminal.""" 422 if self._root is none_value: 423 print("(empty tree)") 424 return 425 426 lines, *_ = self._pretty_print_helper(self._root, none_value) 427 for line in lines: 428 print(line) 429 430 def _pretty_print_helper(self, node, none_value=None): 431 """Returns list of strings, width, height, and horizontal coordinate of the root.""" 432 if node._right_child is none_value and node._left_child is none_value: 433 line = str(node._value) 434 width = len(line) 435 height = 1 436 middle = width // 2 437 return [line], width, height, middle 438 439 # Only left child 440 if node._right_child is none_value: 441 lines, n, p, x = self._pretty_print_helper(node._left_child, none_value) 442 s = str(node._value) 443 u = len(s) 444 first_line = (x + 1) * ' ' + (n - x - 1) * '_' + s 445 second_line = x * ' ' + '/' + (n - x - 1 + u) * ' ' 446 shifted_lines = [line + u * ' ' for line in lines] 447 return [first_line, second_line] + shifted_lines, n + u, p + 2, n + u // 2 448 449 # Only right child 450 if node._left_child is none_value: 451 lines, n, p, x = self._pretty_print_helper(node._right_child, none_value) 452 s = str(node._value) 453 u = len(s) 454 first_line = s + x * '_' + (n - x) * ' ' 455 second_line = (u + x) * ' ' + '\\' + (n - x - 1) * ' ' 456 shifted_lines = [u * ' ' + line for line in lines] 457 return [first_line, second_line] + shifted_lines, n + u, p + 2, u // 2 458 459 # Two children 460 left, n, p, x = self._pretty_print_helper(node._left_child, none_value) 461 right, m, q, y = self._pretty_print_helper(node._right_child, none_value) 462 s = str(node._value) 463 u = len(s) 464 first_line = (x + 1) * ' ' + (n - x - 1) * '_' + s + y * '_' + (m - y) * ' ' 465 second_line = x * ' ' + '/' + (n - x - 1 + u + y) * ' ' + '\\' + (m - y - 1) * ' ' 466 if p < q: 467 left += [n * ' '] * (q - p) 468 elif q < p: 469 right += [m * ' '] * (p - q) 470 zipped_lines = zip(left, right) 471 lines = [first_line, second_line] + [a + u * ' ' + b for a, b in zipped_lines] 472 return lines, n + m + u, max(p, q) + 2, n + u // 2
73 def inorder_traversal(self, output_type=default_keyword): 74 self._check_param_type(output_type, traversal_keywords) 75 self._check_empty() 76 77 if output_type == "list": 78 return list(node.value for node in self._inorder(self.root)) 79 elif output_type == "print": 80 for i in self._inorder(self.root): 81 print(i.value) 82 elif output_type == "generator": 83 return self._inorder(self.root)
92 def preorder_traversal(self, output_type=default_keyword): 93 self._check_param_type(output_type, traversal_keywords) 94 self._check_empty() 95 96 if output_type == "list": 97 return list(node.value for node in self._preorder(self.root)) 98 elif output_type == "print": 99 for i in self._preorder(self.root): 100 print(i.value) 101 elif output_type == "generator": 102 return self._preorder(self.root)
111 def postorder_traversal(self, output_type=default_keyword): 112 self._check_param_type(output_type, traversal_keywords) 113 self._check_empty() 114 115 if output_type == "list": 116 return list(node.value for node in self._postorder(self.root)) 117 elif output_type == "print": 118 for i in self._postorder(self.root): 119 print(i.value) 120 elif output_type == "generator": 121 return self._postorder(self.root)
130 def levelorder_traversal(self, none_value=None): 131 if self.root is none_value: 132 return none_value 133 queue = QueueArray(self.root) 134 result = [] 135 while queue.size != 0: 136 current_node = queue.dequeue() 137 result.append(current_node.value) 138 if current_node.left_child is not none_value: 139 queue.enqueue(current_node.left_child) 140 if current_node.right_child is not none_value: 141 queue.enqueue(current_node.right_child) 142 return result
144 def insert(self, data, node=None): 145 if not isinstance(data, self._Node): 146 new_node = self._Node(data) 147 else: 148 new_node = data 149 150 if self._root is None: 151 self.root = new_node 152 self._size += 1 153 return new_node 154 else: 155 current_node = self.root if node is None else node 156 if new_node.value >= current_node.value: 157 if current_node.right_child is None: 158 current_node.right_child = new_node 159 new_node.parent = current_node 160 self._size += 1 161 return new_node 162 else: 163 return self.insert(new_node, current_node.right_child) 164 else: 165 if current_node.left_child is None: 166 current_node.left_child = new_node 167 new_node.parent = current_node 168 self._size += 1 169 return new_node 170 else: 171 return self.insert(new_node, current_node.left_child)
173 def insert_iterative(self, data): 174 if not isinstance(data, self._Node): 175 new_node = self._Node(data) 176 else: 177 new_node = data 178 179 if self.root is None: 180 self.root = new_node 181 self._size += 1 182 return new_node 183 else: 184 current_node = self.root 185 while True: 186 if new_node.value >= current_node.value: 187 if current_node.right_child is None: 188 current_node.right_child = new_node 189 new_node.parent = current_node 190 self._size += 1 191 return new_node 192 current_node = current_node.right_child 193 elif new_node.value < current_node.value: 194 if current_node.left_child is None: 195 current_node.left_child = new_node 196 new_node.parent = current_node 197 self._size += 1 198 return new_node 199 current_node = current_node.left_child
201 def search_element(self, value, node=None): 202 if not isinstance(value, self._Node): 203 searched_node = self._Node(value) 204 else: 205 searched_node = value 206 current_node = self.root if node is None else node 207 208 if self._root is None: 209 raise Exception("Binary search tree is empty") 210 elif current_node.value == searched_node.value: 211 return current_node 212 else: 213 if searched_node.value < current_node.value: 214 if current_node.left_child is None: 215 return None 216 return self.search_element(searched_node, current_node.left_child) 217 else: 218 if current_node.right_child is None: 219 return None 220 return self.search_element(searched_node, current_node.right_child)
222 def search_iterative(self, value, none_value=None): 223 if self._root is none_value: 224 raise Exception("Binary search tree is empty") 225 current_node = self.root 226 while current_node is not none_value: 227 if current_node.value == value: 228 return current_node 229 elif current_node.value >= value: 230 current_node = current_node.left_child 231 else: 232 current_node = current_node.right_child 233 raise Exception(f"Value {value} is not found")
235 def delete_node(self, value, node=None): 236 if node is None: 237 node = self.search_element(value, node=node) # returns None if element couldn't be found 238 if node is None: 239 raise Exception(f"{value} does not exist in the tree") 240 241 parent_node = node.parent 242 # deleting a node that has no children 243 if node.left_child is None and node.right_child is None: 244 if parent_node.left_child == node: 245 parent_node.left_child = None 246 elif parent_node.right_child == node: 247 parent_node.right_child = None 248 self._size -= 1 249 # deleting a node that has only one child 250 elif node.left_child is None and node.right_child is not None: 251 if parent_node.left_child == node: 252 parent_node.left_child = node.right_child 253 else: 254 parent_node.right_child = node.right_child 255 self._size -= 1 256 elif node.left_child is not None and node.right_child is None: 257 if parent_node.left_child == node: 258 parent_node.left_child = node.left_child 259 else: 260 parent_node.right_child = node.left_child 261 self._size -= 1 262 # deleting a node with two children (using in order successor) 263 else: 264 in_order_successor = node.right_child 265 while in_order_successor.left_child: 266 in_order_successor = in_order_successor.left_child 267 node.value = in_order_successor.value 268 # now there are two nodes with the same value. when self.delete() called, \ 269 # as a default value None, the search_element() method will start looking \ 270 # by the root node. every time this will end up in the first occurence of the \ 271 # same value. to avoid this, we can start from node.right_child but we already \ 272 # have access to the real in_order_successor, so the function below in fact \ 273 # will take O(1) time. 274 self.delete_node(in_order_successor.value, in_order_successor)
276 def delete_iterative(self, value): 277 node = self._root 278 parent_node = None 279 280 while node is not None and node.value != value: 281 parent_node = node 282 if node.value > value: 283 node = node.left_child 284 else: 285 node = node.right_child 286 287 if node is None: 288 raise Exception(f"{value} is not found in the tree") 289 290 # deleting a node that has no children 291 if node.left_child is None and node.right_child is None: 292 if parent_node is None: 293 self.root = None 294 elif parent_node.left_child == node: 295 parent_node.left_child = None 296 elif parent_node.right_child == node: 297 parent_node.right_child = None 298 self._size -= 1 299 # deleting a node that has only one child 300 elif node.left_child is None or node.right_child is None: 301 child_node = node.left_child if node.left_child else node.right_child 302 if parent_node is None: 303 self.root = child_node 304 elif parent_node.left_child == node: 305 parent_node.left_child = child_node 306 else: 307 parent_node.right_child = child_node 308 if child_node: 309 child_node.parent = parent_node 310 # deleting a node with two children (using in order successor) 311 else: 312 successor_parent = node 313 successor = node.right_child 314 while successor.left_child is not None: 315 successor_parent = successor 316 successor = successor.left_child 317 318 node.value = successor.value 319 if successor_parent.left_child == successor: 320 successor_parent.left_child = successor.right_child 321 else: 322 successor_parent.right_child = successor.right_child 323 324 if successor.right_child: # Update parent if successor had a right child 325 successor.right_child.parent = successor_parent 326 self._size -= 1
328 def find_min(self, node=None, return_value=False, none_value=None): 329 self._check_empty() 330 if node is none_value: 331 current_node = self.root 332 else: 333 current_node = node 334 335 while current_node.left_child is not none_value: 336 current_node = current_node.left_child 337 338 if return_value is True: 339 return current_node.value 340 return current_node
342 def find_max(self, node=None, return_value=False, none_value=None): 343 self._check_empty() 344 if node is none_value: 345 current_node = self.root 346 else: 347 current_node = node 348 349 while current_node.right_child is not none_value: 350 current_node = current_node.right_child 351 if return_value is True: 352 return current_node.value 353 return current_node
355 def height(self, opt_node=_sentinel): 356 if opt_node is _sentinel: 357 return self.height(self.root) 358 if opt_node is None: 359 return 0 360 else: 361 left_height = self.height(opt_node.left_child) 362 right_height = self.height(opt_node.right_child) 363 return max(left_height, right_height) + 1
365 def get_size(self): 366 if self.root is None: 367 return 0 368 369 def _size(node=None): 370 if node is None: 371 return 0 372 return _size(node.left_child) + _size(node.right_child) + 1 373 374 ### to be removed later 375 if _size(self.root) != self._size: 376 print("get_size() fonksiyonundan dönüldü. self_size'da hata olmalı") 377 return 378 ### 379 return _size(self.root)
385 def copy(self, method="recursive"): 386 self._check_empty() 387 self._check_param_type(method, copy_keywords) 388 tree = BinarySearchTree() 389 390 def _copy_iterative(): 391 for node in self._preorder(self.root): 392 tree.insert(node.value) 393 return tree 394 395 def _copy_recursive(node, parent): 396 if node is None: 397 return None 398 new_node = self._Node(node.value) 399 new_node.parent = parent 400 tree._size += 1 401 new_node.left_child = _copy_recursive(node.left_child, new_node) 402 new_node.right_child = _copy_recursive(node.right_child, new_node) 403 return new_node 404 405 if method == "iterative": 406 return _copy_iterative() 407 else: 408 tree.root = _copy_recursive(self.root, self.root.parent) 409 return tree
411 def is_balanced(self, node=None): 412 if node is None: 413 node = self.root 414 if not isinstance(node, TreeNode): 415 raise Exception(f"Please provide a {TreeNode} object") 416 left_subtree_height = self.height(node.left_child) 417 right_subtree_height = self.height(node.right_child) 418 return False if abs(left_subtree_height - right_subtree_height) > 1 else True
420 def pretty_print(self, none_value=None): 421 """Prints the tree in a visually appealing way in the terminal.""" 422 if self._root is none_value: 423 print("(empty tree)") 424 return 425 426 lines, *_ = self._pretty_print_helper(self._root, none_value) 427 for line in lines: 428 print(line)
Prints the tree in a visually appealing way in the terminal.
21class AVLTreeNode(TreeNode): 22 """This is the default used node for `AVLTree`. Inherits `TreeNode`. 23 24 Attributes: 25 balance_factor (int): Balance factor of the node (height of the left subree - height of the right subtree). 26 """ 27 def __init__(self, value=None, left_child=None, right_child=None, parent=None, balance_factor=0): 28 super().__init__(value, left_child, right_child, parent) 29 self._balance_factor = balance_factor 30 31 @property 32 def balance_factor(self): 33 """int: Return `balance_factor` attribute of the object.""" 34 return self._balance_factor 35 36 @balance_factor.setter 37 def balance_factor(self, value): 38 """Set the value of the `balance_factor`.""" 39 self._balance_factor = value
This is the default used node for AVLTree. Inherits TreeNode.
Attributes:
- balance_factor (int): Balance factor of the node (height of the left subree - height of the right subtree).
27 def __init__(self, value=None, left_child=None, right_child=None, parent=None, balance_factor=0): 28 super().__init__(value, left_child, right_child, parent) 29 self._balance_factor = balance_factor
Initialize a TreeNode object. Used in data_structures.BinarySearchTree.
Arguments:
- value (Any): The stored value in the node.
- left_child (Node | None): Left child of the current node.
- right_child (Node | None): Right child of the current node.
- parent (Node | None): Parent node of the current node.
31 @property 32 def balance_factor(self): 33 """int: Return `balance_factor` attribute of the object.""" 34 return self._balance_factor
int: Return balance_factor attribute of the object.
41class AVLTree(BinarySearchTree): 42 """AVL Trees are self-balancing binary search trees. Thus, `AVLTree` object inherits `BinarySearchTree` class.""" 43 44 def __init__(self, value=None, node=AVLTreeNode): 45 """Initialize an AVL Tree. 46 47 Args: 48 value (any): Value of the root node (if exists). 49 node (any): Default node to construct the tree. Defaults to `AVLTreeNode`. 50 """ 51 self._Node = node 52 if value is None: 53 self.root = None 54 self._size = 0 55 else: 56 self._root = self._Node(value) 57 self._size = 1 58 59 def calculate_balance_factor(self, node=_sentinel): 60 if node is _sentinel: 61 return self.height(self.root.left_child) - self.height(self.root.right_child) 62 else: 63 return self.height(node.left_child) - self.height(node.right_child) 64 65 def insert(self, data): 66 node = self.insert_iterative(data) 67 self._rebalance_path(node) 68 69 def _rebalance_path(self, node, deletion=False): 70 while node is not None: 71 node.balance_factor = self.calculate_balance_factor(node) 72 if abs(node.balance_factor) > 1: 73 self._rebalance(node, deletion) 74 node = node.parent 75 76 def _rebalance(self, node, deletion=False): 77 case = self._get_rotation_case(node, deletion) 78 match case: 79 case "LL": 80 logging.info(f"Case {case} at node {node.value}, pivot={self._get_pivot(node, case).value}") 81 self._rotate_right(node) 82 case "LR": 83 logging.info(f"Case {case} at node {node.value}, pivot={self._get_pivot(node, case).value}") 84 self._rotate_left_right(node) 85 case "RR": 86 logging.info(f"Case {case} at node {node.value}, pivot={self._get_pivot(node, case).value}") 87 self._rotate_left(node) 88 case "RL": 89 logging.info(f"Case {case} at node {node.value}, pivot={self._get_pivot(node, case).value}") 90 self._rotate_right_left(node) 91 case "R0": 92 logging.info(f"Case {case} at node {node.value}, " 93 f"node.bf = {node.balance_factor}, " 94 f"child.bf = {node.right_child.balance_factor}" 95 ) 96 self._rotate_right(node) 97 case "R-1": 98 logging.info(f"Case {case} at node {node.value}, " 99 f"node.bf = {node.balance_factor}, " 100 f"child.bf = {node.right_child.balance_factor}" 101 ) 102 self._rotate_left_right(node) 103 case "R1": 104 logging.info(f"Case {case} at node {node.value}, " 105 f"node.bf = {node.balance_factor}, " 106 f"child.bf = {node.right_child.balance_factor}" 107 ) 108 self._rotate_right(node) 109 case "L0": 110 logging.info(f"Case {case} at node {node.value}, " 111 f"node.bf = {node.balance_factor}, " 112 f"child.bf = {node.right_child.balance_factor}" 113 ) 114 self._rotate_left(node) 115 case "L-1": 116 logging.info(f"Case {case} at node {node.value}, " 117 f"node.bf = {node.balance_factor}, " 118 f"child.bf = {node.right_child.balance_factor}" 119 ) 120 self._rotate_right_left(node) 121 case "L1": 122 logging.info(f"Case {case} at node {node.value}, " 123 f"node.bf = {node.balance_factor}, " 124 f"child.bf = {node.right_child.balance_factor}" 125 ) 126 self._rotate_left(node) 127 128 def _rotate_right(self, node): # LL 129 target_node = node.left_child 130 if node.parent is not None: 131 if node.parent.left_child == node: 132 node.parent.left_child = target_node 133 elif node.parent.right_child == node: 134 node.parent.right_child = target_node 135 else: 136 self.root = target_node 137 self.root.parent = None 138 139 node.left_child = target_node.right_child 140 if target_node.right_child is not None: 141 target_node.right_child.parent = node 142 143 target_node.right_child = node 144 target_node.parent = node.parent 145 node.parent = target_node 146 147 # fix balance factor 148 target_node.balance_factor = self.calculate_balance_factor(target_node) 149 node.balance_factor = self.calculate_balance_factor(node) 150 151 def _rotate_left(self, node): # RR 152 target_node = node.right_child 153 if node.parent is not None: 154 if node.parent.left_child == node: 155 node.parent.left_child = target_node 156 elif node.parent.right_child == node: 157 node.parent.right_child = target_node 158 else: 159 self.root = target_node 160 self.root.parent = None 161 162 node.right_child = target_node.left_child 163 if target_node.left_child is not None: 164 target_node.left_child.parent = node 165 166 target_node.left_child = node 167 target_node.parent = node.parent 168 node.parent = target_node 169 170 # fix balance factor 171 target_node.balance_factor = self.calculate_balance_factor(target_node) 172 node.balance_factor = self.calculate_balance_factor(node) 173 174 def _rotate_left_right(self, node): # LR 175 self._rotate_left(node.left_child) 176 self._rotate_right(node) 177 178 node.balance_factor = self.calculate_balance_factor(node) 179 if node.left_child: 180 node.left_child.balance_factor = self.calculate_balance_factor(node.left_child) 181 pivot = node.parent if node.parent is not None else self.root 182 pivot.balance_factor = self.calculate_balance_factor(pivot) 183 184 def _rotate_right_left(self, node): # RL 185 self._rotate_right(node.right_child) 186 self._rotate_left(node) 187 188 node.balance_factor = self.calculate_balance_factor(node) 189 if node.right_child: 190 node.right_child.balance_factor = self.calculate_balance_factor(node.right_child) 191 pivot = node.parent if node.parent is not None else self.root 192 pivot.balance_factor = self.calculate_balance_factor(pivot) 193 194 def _get_pivot(self, node, case): 195 match case: 196 case "LL": 197 return node.left_child 198 case "LR": 199 return node.left_child.right_child 200 case "RR": 201 return node.right_child 202 case "RL": 203 return node.right_child.left_child 204 205 def _get_rotation_case(self, node, deletion=False): 206 if deletion is True: 207 if node.balance_factor > 1: 208 match node.left_child.balance_factor: 209 case 0: 210 return "R0" 211 case -1: 212 return "R-1" 213 case 1: 214 return "R1" 215 elif node.balance_factor < -1: 216 match node.right_child.balance_factor: 217 case 0: 218 return "L0" 219 case -1: 220 return "L1" 221 case 1: 222 return "L-1" 223 else: # insertion 224 if node.balance_factor > 1: 225 if node.left_child.balance_factor >= 0: 226 return "LL" 227 return "LR" # node.left_child.balance_factor < 0 228 elif node.balance_factor < -1: 229 if node.right_child.balance_factor <= 0: 230 return "RR" 231 return "RL" # node.right_child.balance_factor > 0 232 233 def delete(self, value): 234 node = self.search_element(value) 235 parent_node = node.parent 236 self.delete_node(node.value, node) 237 self._rebalance_path(parent_node, deletion=True)
AVL Trees are self-balancing binary search trees. Thus, AVLTree object inherits BinarySearchTree class.
44 def __init__(self, value=None, node=AVLTreeNode): 45 """Initialize an AVL Tree. 46 47 Args: 48 value (any): Value of the root node (if exists). 49 node (any): Default node to construct the tree. Defaults to `AVLTreeNode`. 50 """ 51 self._Node = node 52 if value is None: 53 self.root = None 54 self._size = 0 55 else: 56 self._root = self._Node(value) 57 self._size = 1
Initialize an AVL Tree.
Arguments:
- value (any): Value of the root node (if exists).
- node (any): Default node to construct the tree. Defaults to
AVLTreeNode.
19class RBTreeNode(TreeNode): 20 21 def __init__(self, value, left_child=None, right_child=None, color=Color.RED, parent=None, nil_node=None): 22 super().__init__(value, left_child, right_child, parent) 23 self._color = color 24 self.NIL = nil_node 25 26 @property 27 def color(self): 28 return self._color 29 30 @color.setter 31 def color(self, color): 32 if not isinstance(color, Color): 33 raise Exception("Wrong type of color. Set either Color.RED or Color.BLACK") 34 self._color = color 35 36 @property 37 def is_red(self): 38 return self._color == Color.RED 39 40 @property 41 def is_black(self): 42 return self._color == Color.BLACK 43 44 @property 45 def grandparent(self): 46 if self.parent is self.NIL or self.parent.parent is self.NIL: 47 return self.NIL 48 return self.parent.parent 49 50 @property 51 def uncle(self): 52 grand_parent = self.grandparent 53 if grand_parent is self.NIL: 54 return self.NIL 55 elif grand_parent.left_child == self.parent: 56 return grand_parent.right_child 57 else: 58 return grand_parent.left_child 59 60 def is_left_child(self): 61 return self.parent != self.NIL and self.parent.left_child == self 62 63 def is_right_child(self): 64 return self.parent != self.NIL and self.parent.right_child == self
This is the base node used when implementing binary search trees.
Arguments:
- value (Any): The stored value in the node.
- left_child (Node | None): Left child of the current node.
- right_child (Node | None): Right child of the current node.
- parent (Node | None): Parent node of the current node.
21 def __init__(self, value, left_child=None, right_child=None, color=Color.RED, parent=None, nil_node=None): 22 super().__init__(value, left_child, right_child, parent) 23 self._color = color 24 self.NIL = nil_node
Initialize a TreeNode object. Used in data_structures.BinarySearchTree.
Arguments:
- value (Any): The stored value in the node.
- left_child (Node | None): Left child of the current node.
- right_child (Node | None): Right child of the current node.
- parent (Node | None): Parent node of the current node.
66class RedBlackTree(BinarySearchTree): 67 def __init__(self, value=None, node=RBTreeNode): 68 super().__init__(value, node) 69 self._Node = node 70 self.NIL = self._Node(None, color=Color.BLACK) 71 self.NIL._left_child = self.NIL 72 self.NIL._right_child = self.NIL 73 self.NIL.parent = self.NIL 74 75 if value is None: 76 self._root = self.NIL 77 self._size = 0 78 else: 79 self._root = self._Node(value, color=Color.BLACK, parent=self.NIL, 80 left_child=self.NIL, right_child=self.NIL) 81 self._size = 1 82 83 def __iter__(self): 84 for node in self._inorder(self.root): 85 yield node 86 87 def _inorder(self, node): 88 if node is self.NIL: 89 return 90 yield from self._inorder(node.left_child) 91 yield node 92 yield from self._inorder(node.right_child) 93 94 def _preorder(self, node): 95 if node is self.NIL: 96 return 97 yield node 98 yield from self._preorder(node.left_child) 99 yield from self._preorder(node.right_child) 100 101 def _postorder(self, node): 102 if node is self.NIL: 103 return 104 yield from self._postorder(node.left_child) 105 yield from self._postorder(node.right_child) 106 yield node 107 108 def insert(self, data): 109 node = self._insert_iterative(data) 110 if self.root is node: 111 node.color = Color.BLACK 112 self._fix_insertion(node) 113 114 def _insert_iterative(self, data): 115 if not isinstance(data, self._Node): 116 new_node = self._Node(data, left_child=self.NIL, right_child=self.NIL, parent=self.NIL, nil_node=self.NIL) 117 else: 118 new_node = data 119 120 if self.root is self.NIL: 121 self.root = new_node 122 self._size += 1 123 return new_node 124 else: 125 current_node = self.root 126 while True: 127 if new_node.value >= current_node.value: 128 if current_node.right_child is self.NIL: 129 current_node.right_child = new_node 130 new_node.parent = current_node 131 self._size += 1 132 return new_node 133 current_node = current_node.right_child 134 elif new_node.value < current_node.value: 135 if current_node.left_child is self.NIL: 136 current_node.left_child = new_node 137 new_node.parent = current_node 138 self._size += 1 139 return new_node 140 current_node = current_node.left_child 141 142 def _fix_insertion(self, node): 143 while node.parent.is_red: 144 if node.parent.is_left_child(): 145 if node.uncle.color == Color.RED: 146 node.parent.color = Color.BLACK 147 node.uncle.color = Color.BLACK 148 node.grandparent.color = Color.RED 149 node = node.grandparent 150 else: # if node.uncle.color == Color.BLACK or node.uncle is None 151 if node.is_right_child(): 152 node = node.parent 153 self._rotate_left(node) 154 node.parent.color = Color.BLACK 155 node.grandparent.color = Color.RED 156 self._rotate_right(node.grandparent) 157 else: # node.parent.is_right_child() 158 if node.uncle.color == Color.RED: 159 node.parent.color = Color.BLACK 160 node.uncle.color = Color.BLACK 161 node.grandparent.color = Color.RED 162 node = node.grandparent 163 else: # if node.uncle.color == Color.BLACK or node.uncle is None 164 if node.is_left_child(): 165 node = node.parent 166 self._rotate_right(node) 167 node.parent.color = Color.BLACK 168 node.grandparent.color = Color.RED 169 self._rotate_left(node.grandparent) 170 171 self.root.color = Color.BLACK 172 173 def _rotate_right(self, node): 174 target_node = node.left_child 175 if node.parent is not self.NIL: 176 if node.parent.left_child == node: 177 node.parent.left_child = target_node 178 elif node.parent.right_child == node: 179 node.parent.right_child = target_node 180 else: 181 self.root = target_node 182 self.root.parent = self.NIL 183 184 node.left_child = target_node.right_child 185 if target_node.right_child is not self.NIL: 186 target_node.right_child.parent = node 187 188 target_node.right_child = node 189 temp = node.parent 190 target_node.parent = temp 191 node.parent = target_node 192 193 def _rotate_left(self, node): 194 target_node = node.right_child 195 if node.parent is not self.NIL: 196 if node.parent.left_child == node: 197 node.parent.left_child = target_node 198 elif node.parent.right_child == node: 199 node.parent.right_child = target_node 200 else: 201 self.root = target_node 202 self.root.parent = self.NIL 203 204 node.right_child = target_node.left_child 205 if target_node.left_child is not self.NIL: 206 target_node.left_child.parent = node 207 208 target_node.left_child = node 209 temp = node.parent 210 target_node.parent = temp 211 node.parent = target_node 212 213 def delete(self, node): 214 deleted_node = self.search_iterative(node, self.NIL) 215 node_color = deleted_node.color 216 if deleted_node.left_child is self.NIL: 217 node = deleted_node.right_child 218 self._transplant(deleted_node, deleted_node.right_child) 219 elif deleted_node.right_child is self.NIL: 220 node = deleted_node.left_child 221 self._transplant(deleted_node, deleted_node.left_child) 222 else: # node has both children 223 successor = self.find_min(deleted_node.right_child, none_value=self.NIL) 224 node_color = successor.color 225 node = successor.right_child 226 227 if successor.parent != deleted_node: 228 self._transplant(successor, successor.right_child) 229 successor.right_child = deleted_node.right_child 230 successor.right_child.parent = successor 231 232 self._transplant(deleted_node, successor) 233 successor.left_child = deleted_node.left_child 234 successor.left_child.parent = successor 235 successor.color = deleted_node.color 236 237 if node_color == Color.BLACK: 238 self._fix_deletion(node) 239 240 241 def _transplant(self, u, v): 242 """Replaces subtree rooted at u with subtree rooted at v.""" 243 if u.parent == self.NIL: 244 self.root = v 245 elif u == u.parent.left_child: 246 u.parent.left_child = v 247 else: 248 u.parent.right_child = v 249 v.parent = u.parent 250 251 def _fix_deletion(self, node): 252 while node != self.root and node.color == Color.BLACK: 253 if node.is_left_child(): 254 sibling_node = node.parent.right_child 255 if sibling_node.color == Color.RED: 256 sibling_node.color = Color.BLACK 257 sibling_node.parent.color = Color.RED 258 self._rotate_left(sibling_node.parent) 259 sibling_node = node.parent.right_child 260 elif sibling_node.is_black and sibling_node.left_child.is_black and sibling_node.right_child.is_black: 261 sibling_node.color = Color.RED 262 node = node.parent 263 elif sibling_node.is_black and sibling_node.left_child.is_red and sibling_node.right_child.is_black: 264 sibling_node.left_child.color = Color.BLACK 265 sibling_node.color = Color.RED 266 self._rotate_right(sibling_node) 267 sibling_node = node.parent.right_child 268 else: # sibling_node.is_black and sibling_node.right_child.is_red 269 sibling_node.color = sibling_node.parent.color 270 sibling_node.parent.color = Color.BLACK 271 sibling_node.right_child.color = Color.BLACK 272 self._rotate_left(sibling_node.parent) 273 node = self.root 274 else: 275 sibling_node = node.parent.left_child 276 if sibling_node.is_red: 277 sibling_node.color = Color.BLACK 278 sibling_node.parent.color = Color.RED 279 self._rotate_right(sibling_node.parent) 280 sibling_node = node.parent.left_child 281 elif sibling_node.is_black and sibling_node.left_child.is_black and sibling_node.right_child.is_black: 282 sibling_node.color = Color.RED 283 node = node.parent 284 elif sibling_node.is_black and sibling_node.right_child.is_red and sibling_node.left_child.is_black: 285 sibling_node.right_child.color = Color.BLACK 286 sibling_node.color = Color.RED 287 self._rotate_left(sibling_node) 288 sibling_node = node.parent.left_child 289 else: # sibling_node.is_black and sibling_node.left_child.is_red 290 sibling_node.color = sibling_node.parent.color 291 sibling_node.parent.color = Color.BLACK 292 sibling_node.left_child.color = Color.BLACK 293 self._rotate_right(sibling_node.parent) 294 node = self.root 295 296 if node: 297 node.color = Color.BLACK 298 self.root.color = Color.BLACK
67 def __init__(self, value=None, node=RBTreeNode): 68 super().__init__(value, node) 69 self._Node = node 70 self.NIL = self._Node(None, color=Color.BLACK) 71 self.NIL._left_child = self.NIL 72 self.NIL._right_child = self.NIL 73 self.NIL.parent = self.NIL 74 75 if value is None: 76 self._root = self.NIL 77 self._size = 0 78 else: 79 self._root = self._Node(value, color=Color.BLACK, parent=self.NIL, 80 left_child=self.NIL, right_child=self.NIL) 81 self._size = 1
213 def delete(self, node): 214 deleted_node = self.search_iterative(node, self.NIL) 215 node_color = deleted_node.color 216 if deleted_node.left_child is self.NIL: 217 node = deleted_node.right_child 218 self._transplant(deleted_node, deleted_node.right_child) 219 elif deleted_node.right_child is self.NIL: 220 node = deleted_node.left_child 221 self._transplant(deleted_node, deleted_node.left_child) 222 else: # node has both children 223 successor = self.find_min(deleted_node.right_child, none_value=self.NIL) 224 node_color = successor.color 225 node = successor.right_child 226 227 if successor.parent != deleted_node: 228 self._transplant(successor, successor.right_child) 229 successor.right_child = deleted_node.right_child 230 successor.right_child.parent = successor 231 232 self._transplant(deleted_node, successor) 233 successor.left_child = deleted_node.left_child 234 successor.left_child.parent = successor 235 successor.color = deleted_node.color 236 237 if node_color == Color.BLACK: 238 self._fix_deletion(node)