dsa.doublylinkedlist
Module containing doubly linked list class.
1""" Module containing doubly linked list class. """ 2 3class Node: 4 """ 5 A doubly linked list node implementation. 6 """ 7 def __init__(self, value): 8 """ 9 Args: 10 value: The value of the node. 11 """ 12 #: value of the node 13 self.value = value 14 #: reference to the next node 15 self.next = None 16 #: reference to the previous node 17 self.prev = None 18 19class DoublyLinkedList: 20 """ 21 A doubly linked list implementation. 22 """ 23 def __init__(self, head: Node|None=None, tail: Node|None=None, count: int=0): 24 """ 25 Initialize a singly linked list. 26 27 if only the head node is specified, tail is set to the head node and count is automatically set to 0. 28 if both head and tail nodes are specified, count should be specified as well. 29 30 Args: 31 head (Node): Reference to the head node. 32 tail (Node): Reference to the tail node. 33 count (int): The number of nodes in the linked list. 34 """ 35 self.head = head 36 if head and tail is None: 37 self.tail = head 38 self.count = 1 39 else: 40 self.tail = tail 41 self.count = count 42 43 @classmethod 44 def from_list(cls, mylist: list): 45 """ 46 Create a doubly linked list from a list. 47 48 Args: 49 mylist: A list or container to convert from. 50 51 Returns: 52 Doubly linked list with the contents of the list. 53 """ 54 dll = cls() 55 for value in mylist: 56 dll.append(value) 57 58 return dll 59 60 def to_list(self) -> list: 61 """ 62 Create a list with contents of the doubly linked list. 63 64 Returns: 65 A list with contents of the doubly linked list. 66 """ 67 mylist = [] 68 current = self.head 69 while current: 70 mylist.append(current.value) 71 current = current.next 72 return mylist 73 74 def __repr__(self): 75 """ 76 Return a string representation of the doubly linked list. 77 78 The string representation includes all the values in the list 79 separated by spaces and the total count of nodes in the list. 80 81 Returns: 82 str: A string representation of the list in the format 83 "[ value1 value2 ... valueN] Count: count" 84 """ 85 s = "" 86 current = self.head 87 while current: 88 s += str(current.value) + " " 89 current = current.next 90 91 return f"[ {s}] Count: {self.count}" 92 93 def __getitem__(self, index: int) -> Node: 94 """ 95 Return value at a specified index. Raise exception if index is out of bounds. 96 97 Args: 98 index (int): The index of the value. 99 Raises: 100 IndexError: if index is out of bounds. 101 """ 102 i = 0 103 current = self.head 104 while current: 105 if i == index: 106 return current.value 107 current = current.next 108 i += 1 109 raise IndexError("Index Out of Bounds") 110 111 def __len__(self) -> int: 112 """ 113 Return the number of elements in the doubly linked list. 114 """ 115 return self.count 116 117 def is_empty(self) -> bool: 118 """ 119 Return True if the doubly linked list is empty. 120 """ 121 return self.count == 0 122 123 def print(self): 124 """ 125 Print the contents of the doubly linked list. 126 """ 127 current = self.head 128 while current: 129 print(current.value, end=" ") 130 current = current.next 131 print() 132 133 def print_reverse(self): 134 """ 135 Print the contents of the doubly linked list in reverse order. 136 """ 137 current = self.tail 138 while current: 139 print(current.value, end=" ") 140 current = current.prev 141 print() 142 143 def search(self, value) -> int: 144 """ 145 Search for a value in the linked list. Raise exception if value is not found. 146 147 Args: 148 value: The value to search for in the doubly linked list. 149 150 Returns: 151 Return index of found value. 152 153 Raises: 154 Exception: if value is not found. 155 """ 156 i = 0 157 current = self.head 158 while current: 159 if current.value == value: 160 return i 161 i += 1 162 current = current.next 163 raise Exception("Value not found") 164 165 def insert(self, index: int, value): 166 """ 167 Insert a value at a specified index. Raise exception if index is out of bounds. 168 169 Args: 170 index (int): The index to insert a value. 171 value: The value to insert in the doubly linked list. 172 173 Raises: 174 IndexError: if index is out of bounds. 175 """ 176 177 # insert front 178 if index == 0: 179 self.prepend(value) 180 return 181 elif index == self.count: 182 self.append(value) 183 return 184 elif index > self.count: 185 raise IndexError("Index Out of Bounds") 186 187 # find node to insert after 188 i = 0 189 current = self.head 190 while index < i or current: 191 if i == index: 192 break 193 current = current.next 194 i += 1 195 196 if index > i: 197 raise IndexError("Index Out of Bounds") 198 199 new_node = Node(value) 200 new_node.next = current 201 new_node.prev = current.prev 202 current.prev = new_node 203 new_node.prev.next = new_node 204 205 self.count += 1 206 207 def prepend(self, value): 208 """ 209 Place a value at the beginning of the doubly linked list. 210 211 Args: 212 value: The value to prepend to the doubly linked list. 213 """ 214 new_node = Node(value) 215 new_node.next = self.head 216 self.head.prev = new_node 217 self.head = new_node 218 self.count += 1 219 220 def append(self, value): 221 """ 222 Place a value at the end of the doubly linked list. 223 224 Args: 225 value: The value to append to the doubly linked list. 226 """ 227 if self.head is None: 228 self.head = Node(value) 229 if self.count == 0: 230 self.tail = self.head 231 self.count += 1 232 return 233 234 # go to the end of the list 235 new_node = Node(value) 236 new_node.prev = self.tail 237 self.tail.next = new_node 238 self.tail = new_node 239 240 self.count += 1 241 242 243 def delete(self, index: int): 244 """ 245 Delete a node at a specified index. Raises exception if linked list is empty or if index is invalid. 246 247 Args: 248 index (int): The index of element to be deleted. 249 250 Raises: 251 IndexError: If linked list is empty or index is invalid. 252 """ 253 if self.head is None: 254 raise IndexError("DoublyLinkedList is Empty") 255 256 if index == 0: # Special case: Delete the head node 257 self.delete_head() 258 return 259 elif index == self.count - 1: 260 self.delete_tail() 261 return 262 elif index >= self.count: 263 raise IndexError("Index out of range") 264 265 # Traverse the list to find the node at the specified index 266 current = self.head 267 for i in range(index): 268 if current.next is None: 269 raise IndexError("Index out of range") 270 current = current.next 271 272 # Remove the node by adjusting pointers 273 if current.next: 274 current.next.prev = current.prev 275 else: # If the node to be deleted is the tail (might be redundant) 276 self.tail = current.prev 277 278 if current.prev: 279 current.prev.next = current.next 280 281 self.count -= 1 282 283 def delete_tail(self): 284 """ 285 Delete the tail node of the doubly linked list. 286 287 Raises: 288 IndexError: If linked list is empty. 289 """ 290 if self.tail is None: 291 raise IndexError("DoublyLinkedList is Empty") 292 293 self.tail = self.tail.prev 294 self.tail.next = None 295 self.count -= 1 296 297 def delete_head(self): 298 """ 299 Delete the head node of the doubly linked list. 300 301 Raises: 302 IndexError: If linked list is empty. 303 """ 304 if self.head is None: 305 raise IndexError("DoublyLinkedList is Empty") 306 self.head = self.head.next 307 if self.head: 308 self.head.prev = None 309 else: # If the list becomes empty 310 self.tail = None 311 self.count -= 1 312 313 def __eq__(self, other): 314 """ 315 Compare this doubly linked list to another for equality. 316 317 Args: 318 other: The object to compare with. 319 320 Returns: 321 True if both are DoublyLinkedList instances and their contents are equal, False otherwise. 322 """ 323 if not isinstance(other, DoublyLinkedList): 324 return False 325 return self.to_list() == other.to_list()
4class Node: 5 """ 6 A doubly linked list node implementation. 7 """ 8 def __init__(self, value): 9 """ 10 Args: 11 value: The value of the node. 12 """ 13 #: value of the node 14 self.value = value 15 #: reference to the next node 16 self.next = None 17 #: reference to the previous node 18 self.prev = None
A doubly linked list node implementation.
20class DoublyLinkedList: 21 """ 22 A doubly linked list implementation. 23 """ 24 def __init__(self, head: Node|None=None, tail: Node|None=None, count: int=0): 25 """ 26 Initialize a singly linked list. 27 28 if only the head node is specified, tail is set to the head node and count is automatically set to 0. 29 if both head and tail nodes are specified, count should be specified as well. 30 31 Args: 32 head (Node): Reference to the head node. 33 tail (Node): Reference to the tail node. 34 count (int): The number of nodes in the linked list. 35 """ 36 self.head = head 37 if head and tail is None: 38 self.tail = head 39 self.count = 1 40 else: 41 self.tail = tail 42 self.count = count 43 44 @classmethod 45 def from_list(cls, mylist: list): 46 """ 47 Create a doubly linked list from a list. 48 49 Args: 50 mylist: A list or container to convert from. 51 52 Returns: 53 Doubly linked list with the contents of the list. 54 """ 55 dll = cls() 56 for value in mylist: 57 dll.append(value) 58 59 return dll 60 61 def to_list(self) -> list: 62 """ 63 Create a list with contents of the doubly linked list. 64 65 Returns: 66 A list with contents of the doubly linked list. 67 """ 68 mylist = [] 69 current = self.head 70 while current: 71 mylist.append(current.value) 72 current = current.next 73 return mylist 74 75 def __repr__(self): 76 """ 77 Return a string representation of the doubly linked list. 78 79 The string representation includes all the values in the list 80 separated by spaces and the total count of nodes in the list. 81 82 Returns: 83 str: A string representation of the list in the format 84 "[ value1 value2 ... valueN] Count: count" 85 """ 86 s = "" 87 current = self.head 88 while current: 89 s += str(current.value) + " " 90 current = current.next 91 92 return f"[ {s}] Count: {self.count}" 93 94 def __getitem__(self, index: int) -> Node: 95 """ 96 Return value at a specified index. Raise exception if index is out of bounds. 97 98 Args: 99 index (int): The index of the value. 100 Raises: 101 IndexError: if index is out of bounds. 102 """ 103 i = 0 104 current = self.head 105 while current: 106 if i == index: 107 return current.value 108 current = current.next 109 i += 1 110 raise IndexError("Index Out of Bounds") 111 112 def __len__(self) -> int: 113 """ 114 Return the number of elements in the doubly linked list. 115 """ 116 return self.count 117 118 def is_empty(self) -> bool: 119 """ 120 Return True if the doubly linked list is empty. 121 """ 122 return self.count == 0 123 124 def print(self): 125 """ 126 Print the contents of the doubly linked list. 127 """ 128 current = self.head 129 while current: 130 print(current.value, end=" ") 131 current = current.next 132 print() 133 134 def print_reverse(self): 135 """ 136 Print the contents of the doubly linked list in reverse order. 137 """ 138 current = self.tail 139 while current: 140 print(current.value, end=" ") 141 current = current.prev 142 print() 143 144 def search(self, value) -> int: 145 """ 146 Search for a value in the linked list. Raise exception if value is not found. 147 148 Args: 149 value: The value to search for in the doubly linked list. 150 151 Returns: 152 Return index of found value. 153 154 Raises: 155 Exception: if value is not found. 156 """ 157 i = 0 158 current = self.head 159 while current: 160 if current.value == value: 161 return i 162 i += 1 163 current = current.next 164 raise Exception("Value not found") 165 166 def insert(self, index: int, value): 167 """ 168 Insert a value at a specified index. Raise exception if index is out of bounds. 169 170 Args: 171 index (int): The index to insert a value. 172 value: The value to insert in the doubly linked list. 173 174 Raises: 175 IndexError: if index is out of bounds. 176 """ 177 178 # insert front 179 if index == 0: 180 self.prepend(value) 181 return 182 elif index == self.count: 183 self.append(value) 184 return 185 elif index > self.count: 186 raise IndexError("Index Out of Bounds") 187 188 # find node to insert after 189 i = 0 190 current = self.head 191 while index < i or current: 192 if i == index: 193 break 194 current = current.next 195 i += 1 196 197 if index > i: 198 raise IndexError("Index Out of Bounds") 199 200 new_node = Node(value) 201 new_node.next = current 202 new_node.prev = current.prev 203 current.prev = new_node 204 new_node.prev.next = new_node 205 206 self.count += 1 207 208 def prepend(self, value): 209 """ 210 Place a value at the beginning of the doubly linked list. 211 212 Args: 213 value: The value to prepend to the doubly linked list. 214 """ 215 new_node = Node(value) 216 new_node.next = self.head 217 self.head.prev = new_node 218 self.head = new_node 219 self.count += 1 220 221 def append(self, value): 222 """ 223 Place a value at the end of the doubly linked list. 224 225 Args: 226 value: The value to append to the doubly linked list. 227 """ 228 if self.head is None: 229 self.head = Node(value) 230 if self.count == 0: 231 self.tail = self.head 232 self.count += 1 233 return 234 235 # go to the end of the list 236 new_node = Node(value) 237 new_node.prev = self.tail 238 self.tail.next = new_node 239 self.tail = new_node 240 241 self.count += 1 242 243 244 def delete(self, index: int): 245 """ 246 Delete a node at a specified index. Raises exception if linked list is empty or if index is invalid. 247 248 Args: 249 index (int): The index of element to be deleted. 250 251 Raises: 252 IndexError: If linked list is empty or index is invalid. 253 """ 254 if self.head is None: 255 raise IndexError("DoublyLinkedList is Empty") 256 257 if index == 0: # Special case: Delete the head node 258 self.delete_head() 259 return 260 elif index == self.count - 1: 261 self.delete_tail() 262 return 263 elif index >= self.count: 264 raise IndexError("Index out of range") 265 266 # Traverse the list to find the node at the specified index 267 current = self.head 268 for i in range(index): 269 if current.next is None: 270 raise IndexError("Index out of range") 271 current = current.next 272 273 # Remove the node by adjusting pointers 274 if current.next: 275 current.next.prev = current.prev 276 else: # If the node to be deleted is the tail (might be redundant) 277 self.tail = current.prev 278 279 if current.prev: 280 current.prev.next = current.next 281 282 self.count -= 1 283 284 def delete_tail(self): 285 """ 286 Delete the tail node of the doubly linked list. 287 288 Raises: 289 IndexError: If linked list is empty. 290 """ 291 if self.tail is None: 292 raise IndexError("DoublyLinkedList is Empty") 293 294 self.tail = self.tail.prev 295 self.tail.next = None 296 self.count -= 1 297 298 def delete_head(self): 299 """ 300 Delete the head node of the doubly linked list. 301 302 Raises: 303 IndexError: If linked list is empty. 304 """ 305 if self.head is None: 306 raise IndexError("DoublyLinkedList is Empty") 307 self.head = self.head.next 308 if self.head: 309 self.head.prev = None 310 else: # If the list becomes empty 311 self.tail = None 312 self.count -= 1 313 314 def __eq__(self, other): 315 """ 316 Compare this doubly linked list to another for equality. 317 318 Args: 319 other: The object to compare with. 320 321 Returns: 322 True if both are DoublyLinkedList instances and their contents are equal, False otherwise. 323 """ 324 if not isinstance(other, DoublyLinkedList): 325 return False 326 return self.to_list() == other.to_list()
A doubly linked list implementation.
24 def __init__(self, head: Node|None=None, tail: Node|None=None, count: int=0): 25 """ 26 Initialize a singly linked list. 27 28 if only the head node is specified, tail is set to the head node and count is automatically set to 0. 29 if both head and tail nodes are specified, count should be specified as well. 30 31 Args: 32 head (Node): Reference to the head node. 33 tail (Node): Reference to the tail node. 34 count (int): The number of nodes in the linked list. 35 """ 36 self.head = head 37 if head and tail is None: 38 self.tail = head 39 self.count = 1 40 else: 41 self.tail = tail 42 self.count = count
Initialize a singly linked list.
if only the head node is specified, tail is set to the head node and count is automatically set to 0. if both head and tail nodes are specified, count should be specified as well.
Args: head (Node): Reference to the head node. tail (Node): Reference to the tail node. count (int): The number of nodes in the linked list.
44 @classmethod 45 def from_list(cls, mylist: list): 46 """ 47 Create a doubly linked list from a list. 48 49 Args: 50 mylist: A list or container to convert from. 51 52 Returns: 53 Doubly linked list with the contents of the list. 54 """ 55 dll = cls() 56 for value in mylist: 57 dll.append(value) 58 59 return dll
Create a doubly linked list from a list.
Args: mylist: A list or container to convert from.
Returns: Doubly linked list with the contents of the list.
61 def to_list(self) -> list: 62 """ 63 Create a list with contents of the doubly linked list. 64 65 Returns: 66 A list with contents of the doubly linked list. 67 """ 68 mylist = [] 69 current = self.head 70 while current: 71 mylist.append(current.value) 72 current = current.next 73 return mylist
Create a list with contents of the doubly linked list.
Returns: A list with contents of the doubly linked list.
118 def is_empty(self) -> bool: 119 """ 120 Return True if the doubly linked list is empty. 121 """ 122 return self.count == 0
Return True if the doubly linked list is empty.
124 def print(self): 125 """ 126 Print the contents of the doubly linked list. 127 """ 128 current = self.head 129 while current: 130 print(current.value, end=" ") 131 current = current.next 132 print()
Print the contents of the doubly linked list.
134 def print_reverse(self): 135 """ 136 Print the contents of the doubly linked list in reverse order. 137 """ 138 current = self.tail 139 while current: 140 print(current.value, end=" ") 141 current = current.prev 142 print()
Print the contents of the doubly linked list in reverse order.
144 def search(self, value) -> int: 145 """ 146 Search for a value in the linked list. Raise exception if value is not found. 147 148 Args: 149 value: The value to search for in the doubly linked list. 150 151 Returns: 152 Return index of found value. 153 154 Raises: 155 Exception: if value is not found. 156 """ 157 i = 0 158 current = self.head 159 while current: 160 if current.value == value: 161 return i 162 i += 1 163 current = current.next 164 raise Exception("Value not found")
Search for a value in the linked list. Raise exception if value is not found.
Args: value: The value to search for in the doubly linked list.
Returns: Return index of found value.
Raises: Exception: if value is not found.
166 def insert(self, index: int, value): 167 """ 168 Insert a value at a specified index. Raise exception if index is out of bounds. 169 170 Args: 171 index (int): The index to insert a value. 172 value: The value to insert in the doubly linked list. 173 174 Raises: 175 IndexError: if index is out of bounds. 176 """ 177 178 # insert front 179 if index == 0: 180 self.prepend(value) 181 return 182 elif index == self.count: 183 self.append(value) 184 return 185 elif index > self.count: 186 raise IndexError("Index Out of Bounds") 187 188 # find node to insert after 189 i = 0 190 current = self.head 191 while index < i or current: 192 if i == index: 193 break 194 current = current.next 195 i += 1 196 197 if index > i: 198 raise IndexError("Index Out of Bounds") 199 200 new_node = Node(value) 201 new_node.next = current 202 new_node.prev = current.prev 203 current.prev = new_node 204 new_node.prev.next = new_node 205 206 self.count += 1
Insert a value at a specified index. Raise exception if index is out of bounds.
Args: index (int): The index to insert a value. value: The value to insert in the doubly linked list.
Raises: IndexError: if index is out of bounds.
208 def prepend(self, value): 209 """ 210 Place a value at the beginning of the doubly linked list. 211 212 Args: 213 value: The value to prepend to the doubly linked list. 214 """ 215 new_node = Node(value) 216 new_node.next = self.head 217 self.head.prev = new_node 218 self.head = new_node 219 self.count += 1
Place a value at the beginning of the doubly linked list.
Args: value: The value to prepend to the doubly linked list.
221 def append(self, value): 222 """ 223 Place a value at the end of the doubly linked list. 224 225 Args: 226 value: The value to append to the doubly linked list. 227 """ 228 if self.head is None: 229 self.head = Node(value) 230 if self.count == 0: 231 self.tail = self.head 232 self.count += 1 233 return 234 235 # go to the end of the list 236 new_node = Node(value) 237 new_node.prev = self.tail 238 self.tail.next = new_node 239 self.tail = new_node 240 241 self.count += 1
Place a value at the end of the doubly linked list.
Args: value: The value to append to the doubly linked list.
244 def delete(self, index: int): 245 """ 246 Delete a node at a specified index. Raises exception if linked list is empty or if index is invalid. 247 248 Args: 249 index (int): The index of element to be deleted. 250 251 Raises: 252 IndexError: If linked list is empty or index is invalid. 253 """ 254 if self.head is None: 255 raise IndexError("DoublyLinkedList is Empty") 256 257 if index == 0: # Special case: Delete the head node 258 self.delete_head() 259 return 260 elif index == self.count - 1: 261 self.delete_tail() 262 return 263 elif index >= self.count: 264 raise IndexError("Index out of range") 265 266 # Traverse the list to find the node at the specified index 267 current = self.head 268 for i in range(index): 269 if current.next is None: 270 raise IndexError("Index out of range") 271 current = current.next 272 273 # Remove the node by adjusting pointers 274 if current.next: 275 current.next.prev = current.prev 276 else: # If the node to be deleted is the tail (might be redundant) 277 self.tail = current.prev 278 279 if current.prev: 280 current.prev.next = current.next 281 282 self.count -= 1
Delete a node at a specified index. Raises exception if linked list is empty or if index is invalid.
Args: index (int): The index of element to be deleted.
Raises: IndexError: If linked list is empty or index is invalid.
284 def delete_tail(self): 285 """ 286 Delete the tail node of the doubly linked list. 287 288 Raises: 289 IndexError: If linked list is empty. 290 """ 291 if self.tail is None: 292 raise IndexError("DoublyLinkedList is Empty") 293 294 self.tail = self.tail.prev 295 self.tail.next = None 296 self.count -= 1
Delete the tail node of the doubly linked list.
Raises: IndexError: If linked list is empty.
298 def delete_head(self): 299 """ 300 Delete the head node of the doubly linked list. 301 302 Raises: 303 IndexError: If linked list is empty. 304 """ 305 if self.head is None: 306 raise IndexError("DoublyLinkedList is Empty") 307 self.head = self.head.next 308 if self.head: 309 self.head.prev = None 310 else: # If the list becomes empty 311 self.tail = None 312 self.count -= 1
Delete the head node of the doubly linked list.
Raises: IndexError: If linked list is empty.