pure-python-ds 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. pure_python_ds-1.0.0/LICENSE +21 -0
  2. pure_python_ds-1.0.0/PKG-INFO +69 -0
  3. pure_python_ds-1.0.0/README.md +52 -0
  4. pure_python_ds-1.0.0/pure_python_ds/__init__.py +0 -0
  5. pure_python_ds-1.0.0/pure_python_ds/algorithms/__init__.py +3 -0
  6. pure_python_ds-1.0.0/pure_python_ds/algorithms/dp.py +16 -0
  7. pure_python_ds-1.0.0/pure_python_ds/algorithms/searching.py +22 -0
  8. pure_python_ds-1.0.0/pure_python_ds/algorithms/sorting.py +36 -0
  9. pure_python_ds-1.0.0/pure_python_ds/graphs/__init__.py +2 -0
  10. pure_python_ds-1.0.0/pure_python_ds/graphs/dsu.py +22 -0
  11. pure_python_ds-1.0.0/pure_python_ds/graphs/graph.py +106 -0
  12. pure_python_ds-1.0.0/pure_python_ds/linear/__init__.py +6 -0
  13. pure_python_ds-1.0.0/pure_python_ds/linear/doubly_linked_list.py +91 -0
  14. pure_python_ds-1.0.0/pure_python_ds/linear/hash_table.py +33 -0
  15. pure_python_ds-1.0.0/pure_python_ds/linear/heap.py +102 -0
  16. pure_python_ds-1.0.0/pure_python_ds/linear/queue.py +24 -0
  17. pure_python_ds-1.0.0/pure_python_ds/linear/singly_linked_list.py +106 -0
  18. pure_python_ds-1.0.0/pure_python_ds/linear/stack.py +28 -0
  19. pure_python_ds-1.0.0/pure_python_ds/nodes/__init__.py +25 -0
  20. pure_python_ds-1.0.0/pure_python_ds/nodes/b_tree_node.py +9 -0
  21. pure_python_ds-1.0.0/pure_python_ds/nodes/rb_node.py +16 -0
  22. pure_python_ds-1.0.0/pure_python_ds/trees/__init__.py +7 -0
  23. pure_python_ds-1.0.0/pure_python_ds/trees/avl_tree.py +142 -0
  24. pure_python_ds-1.0.0/pure_python_ds/trees/b_tree.py +55 -0
  25. pure_python_ds-1.0.0/pure_python_ds/trees/binary_search_tree.py +88 -0
  26. pure_python_ds-1.0.0/pure_python_ds/trees/binary_tree.py +69 -0
  27. pure_python_ds-1.0.0/pure_python_ds/trees/red_black_tree.py +111 -0
  28. pure_python_ds-1.0.0/pure_python_ds/trees/segment_tree.py +35 -0
  29. pure_python_ds-1.0.0/pure_python_ds/trees/trie.py +50 -0
  30. pure_python_ds-1.0.0/pure_python_ds/trees/utils.py +26 -0
  31. pure_python_ds-1.0.0/pure_python_ds.egg-info/PKG-INFO +69 -0
  32. pure_python_ds-1.0.0/pure_python_ds.egg-info/SOURCES.txt +37 -0
  33. pure_python_ds-1.0.0/pure_python_ds.egg-info/dependency_links.txt +1 -0
  34. pure_python_ds-1.0.0/pure_python_ds.egg-info/top_level.txt +1 -0
  35. pure_python_ds-1.0.0/pyproject.toml +28 -0
  36. pure_python_ds-1.0.0/setup.cfg +4 -0
  37. pure_python_ds-1.0.0/tests/test_avl_guaranteed.py +21 -0
  38. pure_python_ds-1.0.0/tests/test_core.py +458 -0
  39. pure_python_ds-1.0.0/tests/test_coverage_completion.py +171 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Parjad Minooei
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: pure-python-ds
3
+ Version: 1.0.0
4
+ Summary: A strictly typed, 100% test-covered data structures and algorithms library.
5
+ Author: Parjad Minooei
6
+ Project-URL: Documentation, https://ParjadM.github.io/pure-python-ds/
7
+ Project-URL: Source, https://github.com/ParjadM/pure-python-ds
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
+ Requires-Python: >=3.12
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Dynamic: license-file
17
+
18
+ # pure-python-ds 🚀
19
+
20
+ A high-performance, strictly-typed, and memory-optimized Data Structures and Algorithms library built in Pure Python.
21
+
22
+ ## 🏗️ Architecture Overview
23
+
24
+ This library was designed with a focus on **Systems Architecture**. By utilizing Python's `__slots__`, every node in this library is memory-optimized to reduce overhead, making it suitable for educational deep-dives and performance-sensitive prototyping.
25
+
26
+ [Image of a software architecture diagram showing layers of data structures and algorithms]
27
+
28
+ ## 🛠️ Key Features
29
+
30
+ ### 1. Linear Data Structures
31
+ * **Linked Lists:** Singly and Doubly Linked Lists with $O(1)$ head/tail operations.
32
+ * **Stacks & Queues:** Built on top of optimized linked nodes for strict LIFO/FIFO behavior.
33
+
34
+ ### 2. Hierarchical & Network Structures
35
+ * **Binary Search Trees (BST):** Standard ordered trees with recursive and iterative implementations.
36
+ * **AVL Trees:** Self-balancing trees using rotation logic to guarantee $O(\log n)$ performance.
37
+ * **Graphs:** Adjacency-list based implementation supporting both Directed and Undirected networks.
38
+
39
+ ### 3. Advanced Algorithms
40
+ * **Pathfinding:** Dijkstra's Algorithm for finding shortest paths in weighted graphs.
41
+ * **Sorting:** $O(n \log n)$ Merge Sort implementation.
42
+ * **Searching:** $O(\log n)$ Binary Search.
43
+ * **Dynamic Programming:** Memoized Fibonacci and sub-problem optimization.
44
+
45
+ [Image of a perfectly balanced Binary Search Tree demonstrating O(log n) structure]
46
+
47
+ ## 🚀 Installation
48
+
49
+ Since the package is in "Editable" mode, you can install it locally to use across your entire system:
50
+
51
+ ```bash
52
+ git clone [https://github.com/ParjadM/pure-python-ds.git](https://github.com/ParjadM/pure-python-ds.git)
53
+ cd pure-python-ds
54
+ pip install -e .
55
+ ```
56
+
57
+ 🚀 Technical Milestone: 83% Unit Test Coverage Reached
58
+ Core Engineering Accomplishments:
59
+
60
+ Robustness Verification: Engineered a comprehensive regression suite achieving 83% total coverage across 15+ complex data structures.
61
+
62
+ Algorithm Integration: Successfully implemented and verified Kruskal’s MST (using custom DSU), Bellman-Ford, and Dijkstra’s algorithms.
63
+
64
+ Recursive Tree Logic: Developed and tested memory-safe recursive deletion with rebalancing for AVL and Binary Search Trees.
65
+
66
+ Performance Optimization: Leveraged __slots__ and Python Generators to ensure O(1) space complexity for tree traversals and minimal memory footprint.
67
+
68
+ ![Coverage](https://img.shields.io/badge/Coverage-83%25-brightgreen?style=for-the-badge&logo=pytest)
69
+ ![Build](https://img.shields.io/badge/Build-Passing-success?style=for-the-badge&logo=github-actions)
@@ -0,0 +1,52 @@
1
+ # pure-python-ds 🚀
2
+
3
+ A high-performance, strictly-typed, and memory-optimized Data Structures and Algorithms library built in Pure Python.
4
+
5
+ ## 🏗️ Architecture Overview
6
+
7
+ This library was designed with a focus on **Systems Architecture**. By utilizing Python's `__slots__`, every node in this library is memory-optimized to reduce overhead, making it suitable for educational deep-dives and performance-sensitive prototyping.
8
+
9
+ [Image of a software architecture diagram showing layers of data structures and algorithms]
10
+
11
+ ## 🛠️ Key Features
12
+
13
+ ### 1. Linear Data Structures
14
+ * **Linked Lists:** Singly and Doubly Linked Lists with $O(1)$ head/tail operations.
15
+ * **Stacks & Queues:** Built on top of optimized linked nodes for strict LIFO/FIFO behavior.
16
+
17
+ ### 2. Hierarchical & Network Structures
18
+ * **Binary Search Trees (BST):** Standard ordered trees with recursive and iterative implementations.
19
+ * **AVL Trees:** Self-balancing trees using rotation logic to guarantee $O(\log n)$ performance.
20
+ * **Graphs:** Adjacency-list based implementation supporting both Directed and Undirected networks.
21
+
22
+ ### 3. Advanced Algorithms
23
+ * **Pathfinding:** Dijkstra's Algorithm for finding shortest paths in weighted graphs.
24
+ * **Sorting:** $O(n \log n)$ Merge Sort implementation.
25
+ * **Searching:** $O(\log n)$ Binary Search.
26
+ * **Dynamic Programming:** Memoized Fibonacci and sub-problem optimization.
27
+
28
+ [Image of a perfectly balanced Binary Search Tree demonstrating O(log n) structure]
29
+
30
+ ## 🚀 Installation
31
+
32
+ Since the package is in "Editable" mode, you can install it locally to use across your entire system:
33
+
34
+ ```bash
35
+ git clone [https://github.com/ParjadM/pure-python-ds.git](https://github.com/ParjadM/pure-python-ds.git)
36
+ cd pure-python-ds
37
+ pip install -e .
38
+ ```
39
+
40
+ 🚀 Technical Milestone: 83% Unit Test Coverage Reached
41
+ Core Engineering Accomplishments:
42
+
43
+ Robustness Verification: Engineered a comprehensive regression suite achieving 83% total coverage across 15+ complex data structures.
44
+
45
+ Algorithm Integration: Successfully implemented and verified Kruskal’s MST (using custom DSU), Bellman-Ford, and Dijkstra’s algorithms.
46
+
47
+ Recursive Tree Logic: Developed and tested memory-safe recursive deletion with rebalancing for AVL and Binary Search Trees.
48
+
49
+ Performance Optimization: Leveraged __slots__ and Python Generators to ensure O(1) space complexity for tree traversals and minimal memory footprint.
50
+
51
+ ![Coverage](https://img.shields.io/badge/Coverage-83%25-brightgreen?style=for-the-badge&logo=pytest)
52
+ ![Build](https://img.shields.io/badge/Build-Passing-success?style=for-the-badge&logo=github-actions)
File without changes
@@ -0,0 +1,3 @@
1
+ from .sorting import merge_sort
2
+ from .searching import binary_search
3
+ from .dp import fibonacci
@@ -0,0 +1,16 @@
1
+ from typing import Dict
2
+
3
+ def fibonacci(n: int, memo: Dict[int, int] = None) -> int:
4
+ """
5
+ Computes the nth Fibonacci number using Top-Down DP (Memoization).
6
+ O(n) time complexity vs O(2^n) recursive complexity.
7
+ """
8
+ if memo is None:
9
+ memo = {}
10
+ if n in memo:
11
+ return memo[n]
12
+ if n <= 1:
13
+ return n
14
+
15
+ memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
16
+ return memo[n]
@@ -0,0 +1,22 @@
1
+ from typing import List, TypeVar, Optional, Any
2
+
3
+ T = TypeVar('T')
4
+
5
+ def binary_search(arr: List[Any], target: Any) -> int:
6
+ """
7
+ Performs an iterative binary search on a sorted list.
8
+ Returns the index of the target if found, else -1.
9
+ """
10
+ low = 0
11
+ high = len(arr) - 1
12
+
13
+ while low <= high:
14
+ mid = (low + high) // 2
15
+ if arr[mid] == target:
16
+ return mid
17
+ elif arr[mid] < target:
18
+ low = mid + 1
19
+ else:
20
+ high = mid - 1
21
+
22
+ return -1
@@ -0,0 +1,36 @@
1
+ from typing import List, TypeVar
2
+
3
+ T = TypeVar('T')
4
+
5
+ def merge_sort(arr: List[T]) -> List[T]:
6
+ """
7
+ Perform an O(n log n) Merge Sort on a list.
8
+ Returns a new sorted list.
9
+ """
10
+ if len(arr) <= 1:
11
+ return arr
12
+
13
+ mid = len(arr) // 2
14
+ # Recursive split
15
+ left = merge_sort(arr[:mid])
16
+ right = merge_sort(arr[mid:])
17
+
18
+ return _merge(left, right)
19
+
20
+ def _merge(left: List[T], right: List[T]) -> List[T]:
21
+ """Helper method to merge two sorted lists into one."""
22
+ result = []
23
+ i = j = 0
24
+
25
+ while i < len(left) and j < len(right):
26
+ if left[i] < right[j]:
27
+ result.append(left[i])
28
+ i += 1
29
+ else:
30
+ result.append(right[j])
31
+ j += 1
32
+
33
+ # Add remaining elements
34
+ result.extend(left[i:])
35
+ result.extend(right[j:])
36
+ return result
@@ -0,0 +1,2 @@
1
+ from .graph import Graph
2
+ from .dsu import DSU
@@ -0,0 +1,22 @@
1
+ class DSU:
2
+ def __init__(self, n: int):
3
+ self.parent = list(range(n))
4
+ self.rank = [0] * n
5
+
6
+ def find(self, i: int) -> int:
7
+ if self.parent[i] == i:
8
+ return i
9
+ self.parent[i] = self.find(self.parent[i]) # Path Compression
10
+ return self.parent[i]
11
+
12
+ def union(self, i: int, j: int):
13
+ root_i = self.find(i)
14
+ root_j = self.find(j)
15
+ if root_i != root_j:
16
+ if self.rank[root_i] < self.rank[root_j]:
17
+ self.parent[root_i] = root_j
18
+ elif self.rank[root_i] > self.rank[root_j]:
19
+ self.parent[root_j] = root_i
20
+ else:
21
+ self.parent[root_i] = root_j
22
+ self.rank[root_j] += 1
@@ -0,0 +1,106 @@
1
+ from typing import Generic, TypeVar, Optional, Set, Dict, List, Tuple
2
+ import heapq # We'll use Python's built-in min-heap for O(log n) efficiency here
3
+
4
+ T = TypeVar('T')
5
+
6
+ class Graph(Generic[T]):
7
+ def __init__(self, directed: bool = False):
8
+ # Updated to store {vertex: {neighbor: weight}}
9
+ self._adj_list: Dict[T, Dict[T, float]] = {}
10
+ self.directed = directed
11
+
12
+ def add_vertex(self, vertex: T) -> None:
13
+ if vertex not in self._adj_list:
14
+ self._adj_list[vertex] = {}
15
+
16
+ def add_edge(self, v1: T, v2: T, weight: float = 1.0) -> None:
17
+ """Adds a weighted edge. Defaults to 1.0 if not specified."""
18
+ self.add_vertex(v1)
19
+ self.add_vertex(v2)
20
+ self._adj_list[v1][v2] = weight
21
+ if not self.directed:
22
+ self._adj_list[v2][v1] = weight
23
+
24
+ def dijkstra(self, start: T) -> Dict[T, float]:
25
+ """
26
+ Greedy algorithm to find the shortest distance from 'start' to all other nodes.
27
+ Returns a dictionary of {vertex: min_distance}.
28
+ """
29
+ # Distances from start to all other nodes are initially infinity
30
+ distances = {vertex: float('inf') for vertex in self._adj_list}
31
+ distances[start] = 0
32
+
33
+ # Priority Queue: (distance, vertex)
34
+ pq = [(0, start)]
35
+
36
+ while pq:
37
+ current_distance, current_vertex = heapq.heappop(pq)
38
+
39
+ # If we found a longer path than we already recorded, skip it
40
+ if current_distance > distances[current_vertex]:
41
+ continue
42
+
43
+ for neighbor, weight in self._adj_list[current_vertex].items():
44
+ distance = current_distance + weight
45
+
46
+ # If this path is shorter, update and push to PQ
47
+ if distance < distances[neighbor]:
48
+ distances[neighbor] = distance
49
+ heapq.heappush(pq, (distance, neighbor))
50
+
51
+ return distances
52
+ def kruskal_mst(self) -> List[Tuple[T, T, float]]:
53
+ """
54
+ Finds the Minimum Spanning Tree using the library's DSU module.
55
+ Returns a list of edges (v1, v2, weight) in the MST.
56
+ """
57
+ from pure_python_ds.graphs import DSU
58
+
59
+ edges = []
60
+ # Gather all unique edges
61
+ for u in self._adj_list:
62
+ for v, weight in self._adj_list[u].items():
63
+ if self.directed or (v, u, weight) not in edges:
64
+ edges.append((u, v, weight))
65
+
66
+ # Sort edges by weight
67
+ edges.sort(key=lambda x: x[2])
68
+
69
+ # Map vertices to integers for DSU
70
+ nodes = list(self._adj_list.keys())
71
+ node_to_idx = {node: i for i, node in enumerate(nodes)}
72
+
73
+ dsu = DSU(len(nodes))
74
+ mst = []
75
+
76
+ for u, v, weight in edges:
77
+ if dsu.find(node_to_idx[u]) != dsu.find(node_to_idx[v]):
78
+ dsu.union(node_to_idx[u], node_to_idx[v])
79
+ mst.append((u, v, weight))
80
+
81
+ return mst
82
+ def bellman_ford(self, start: T) -> Dict[T, float]:
83
+ """
84
+ Shortest path algorithm that handles negative weights.
85
+ Returns distances or raises ValueError if a negative cycle is found.
86
+ """
87
+ distances = {vertex: float('inf') for vertex in self._adj_list}
88
+ distances[start] = 0
89
+
90
+ # Relax edges |V| - 1 times
91
+ for _ in range(len(self._adj_list) - 1):
92
+ for u in self._adj_list:
93
+ for v, weight in self._adj_list[u].items():
94
+ if distances[u] + weight < distances[v]:
95
+ distances[v] = distances[u] + weight
96
+
97
+ # Check for negative cycles
98
+ for u in self._adj_list:
99
+ for v, weight in self._adj_list[u].items():
100
+ if distances[u] + weight < distances[v]:
101
+ raise ValueError("Graph contains a negative weight cycle")
102
+
103
+ return distances
104
+ def has_edge(self, u: T, v: T) -> bool:
105
+ """Returns True if there is an edge from u to v."""
106
+ return u in self._adj_list and v in self._adj_list[u]
@@ -0,0 +1,6 @@
1
+ from .singly_linked_list import SinglyLinkedList
2
+ from .doubly_linked_list import DoublyLinkedList
3
+ from .stack import Stack
4
+ from .queue import Queue
5
+ from .heap import MinHeap, MaxHeap
6
+ from .hash_table import HashTable
@@ -0,0 +1,91 @@
1
+ from typing import Generic, TypeVar, Optional, Generator
2
+ from pure_python_ds.nodes import ListNode
3
+
4
+ T = TypeVar('T')
5
+
6
+ class DoublyLinkedList(Generic[T]):
7
+ """A strictly typed, memory-optimized Doubly Linked List."""
8
+
9
+ def __init__(self):
10
+ self.head: Optional[ListNode[T]] = None
11
+ self.tail: Optional[ListNode[T]] = None
12
+ self._length: int = 0
13
+
14
+ def __len__(self) -> int:
15
+ return self._length
16
+
17
+ def append(self, value: T) -> None:
18
+ """Adds a node to the end in O(1) time."""
19
+ new_node = ListNode(value)
20
+ if not self.head or not self.tail:
21
+ self.head = new_node
22
+ self.tail = new_node
23
+ else:
24
+ self.tail.next = new_node
25
+ new_node.prev = self.tail
26
+ self.tail = new_node
27
+ self._length += 1
28
+
29
+ def prepend(self, value: T) -> None:
30
+ """Adds a node to the beginning in O(1) time."""
31
+ new_node = ListNode(value)
32
+ if not self.head or not self.tail:
33
+ self.head = new_node
34
+ self.tail = new_node
35
+ else:
36
+ new_node.next = self.head
37
+ self.head.prev = new_node
38
+ self.head = new_node
39
+ self._length += 1
40
+
41
+ def remove_tail(self) -> Optional[T]:
42
+ """Removes the last node in O(1) time (Impossible in Singly Linked Lists!)."""
43
+ if not self.tail:
44
+ return None
45
+
46
+ value = self.tail.value
47
+ if self.head is self.tail: # Only one element
48
+ self.head = None
49
+ self.tail = None
50
+ else:
51
+ self.tail = self.tail.prev
52
+ self.tail.next = None # Sever the tie
53
+
54
+ self._length -= 1
55
+ return value
56
+
57
+ def __iter__(self) -> Generator[T, None, None]:
58
+ """Forward generator traversal."""
59
+ current = self.head
60
+ while current:
61
+ yield current.value
62
+ current = current.next
63
+
64
+ def __str__(self) -> str:
65
+ """Visual representation showing bidirectional pointers."""
66
+ values = [str(val) for val in self]
67
+ return "None <- " + " <-> ".join(values) + " -> None" if values else "Empty List"
68
+ def remove(self, value: T):
69
+ """Removes the first occurrence of value from the list."""
70
+ current = self.head
71
+ while current:
72
+ if current.value == value:
73
+ # If it's the head
74
+ if current == self.head:
75
+ self.head = current.next
76
+ if self.head:
77
+ self.head.prev = None
78
+ else:
79
+ self.tail = None
80
+ # If it's the tail
81
+ elif current == self.tail:
82
+ self.tail = current.prev
83
+ if self.tail:
84
+ self.tail.next = None
85
+ # If it's in the middle
86
+ else:
87
+ current.prev.next = current.next
88
+ current.next.prev = current.prev
89
+ return True
90
+ current = current.next
91
+ return False
@@ -0,0 +1,33 @@
1
+ from typing import Any, List, Optional
2
+ from pure_python_ds.linear.singly_linked_list import SinglyLinkedList
3
+
4
+ class HashTable:
5
+ def __init__(self, size: int = 10):
6
+ self.size = size
7
+ self.table: List[Optional[SinglyLinkedList]] = [None] * size
8
+
9
+ def _hash(self, key: str) -> int:
10
+ return sum(ord(char) for char in key) % self.size
11
+
12
+ def set(self, key: str, value: Any):
13
+ index = self._hash(key)
14
+ if not self.table[index]:
15
+ self.table[index] = SinglyLinkedList()
16
+
17
+ # Check if key exists to update, else append
18
+ current = self.table[index].head
19
+ while current:
20
+ if current.value[0] == key:
21
+ current.value = (key, value)
22
+ return
23
+ current = current.next
24
+ self.table[index].append((key, value))
25
+
26
+ def get(self, key: str) -> Any:
27
+ index = self._hash(key)
28
+ if not self.table[index]: return None
29
+ current = self.table[index].head
30
+ while current:
31
+ if current.value[0] == key: return current.value[1]
32
+ current = current.next
33
+ return None
@@ -0,0 +1,102 @@
1
+ from typing import List, TypeVar, Generic, Optional
2
+
3
+ T = TypeVar('T')
4
+
5
+ class MinHeap(Generic[T]):
6
+ def __init__(self):
7
+ self.heap: List[T] = []
8
+
9
+ def push(self, val: T) -> None:
10
+ self.heap.append(val)
11
+ self._bubble_up(len(self.heap) - 1)
12
+
13
+ def pop(self) -> Optional[T]:
14
+ if len(self.heap) == 0: return None
15
+ if len(self.heap) == 1: return self.heap.pop()
16
+
17
+ root = self.heap[0]
18
+ self.heap[0] = self.heap.pop()
19
+ self._bubble_down(0)
20
+ return root
21
+
22
+ def _bubble_up(self, index: int):
23
+ parent = (index - 1) // 2
24
+ # Min-Heap check: Child < Parent
25
+ if index > 0 and self.heap[index] < self.heap[parent]:
26
+ self.heap[index], self.heap[parent] = self.heap[parent], self.heap[index]
27
+ self._bubble_up(parent)
28
+
29
+ def _bubble_down(self, index: int):
30
+ smallest = index
31
+ left, right = 2 * index + 1, 2 * index + 2
32
+
33
+ for child in [left, right]:
34
+ if child < len(self.heap) and self.heap[child] < self.heap[smallest]:
35
+ smallest = child
36
+
37
+ if smallest != index:
38
+ self.heap[index], self.heap[smallest] = self.heap[smallest], self.heap[index]
39
+ self._bubble_down(smallest)
40
+
41
+ @property
42
+ def size(self) -> int:
43
+ return len(self.heap)
44
+ @classmethod
45
+ def heapify(cls, data: List[T]) -> 'MinHeap[T]':
46
+ """Transforms an unsorted list into a MinHeap in O(n) time."""
47
+ instance = cls()
48
+ instance.heap = list(data)
49
+ for i in range(len(instance.heap) // 2 - 1, -1, -1):
50
+ instance._bubble_down(i)
51
+ return instance
52
+
53
+ class MaxHeap(Generic[T]):
54
+ """Native Max-Heap to avoid negating values for heapq-like logic."""
55
+ def __init__(self):
56
+ self.heap: List[T] = []
57
+
58
+ def push(self, val: T) -> None:
59
+ self.heap.append(val)
60
+ self._bubble_up(len(self.heap) - 1)
61
+
62
+ def pop(self) -> Optional[T]:
63
+ if len(self.heap) == 0: return None
64
+ if len(self.heap) == 1: return self.heap.pop()
65
+
66
+ root = self.heap[0]
67
+ self.heap[0] = self.heap.pop()
68
+ self._bubble_down(0)
69
+ return root
70
+
71
+ def _bubble_up(self, index: int):
72
+ parent = (index - 1) // 2
73
+ # Max-Heap check: Child > Parent
74
+ if index > 0 and self.heap[index] > self.heap[parent]:
75
+ self.heap[index], self.heap[parent] = self.heap[parent], self.heap[index]
76
+ self._bubble_up(parent)
77
+
78
+ def _bubble_down(self, index: int):
79
+ largest = index
80
+ left, right = 2 * index + 1, 2 * index + 2
81
+
82
+ for child in [left, right]:
83
+ # Max-Heap check: Child > Parent
84
+ if child < len(self.heap) and self.heap[child] > self.heap[largest]:
85
+ largest = child
86
+
87
+ if largest != index:
88
+ self.heap[index], self.heap[largest] = self.heap[largest], self.heap[index]
89
+ self._bubble_down(largest)
90
+
91
+ @property
92
+ def size(self) -> int:
93
+ return len(self.heap)
94
+
95
+ @classmethod
96
+ def heapify(cls, data: List[T]) -> 'MaxHeap[T]':
97
+ """Transforms an unsorted list into a MaxHeap in O(n) time."""
98
+ instance = cls()
99
+ instance.heap = list(data)
100
+ for i in range(len(instance.heap) // 2 - 1, -1, -1):
101
+ instance._bubble_down(i)
102
+ return instance
@@ -0,0 +1,24 @@
1
+ from typing import Generic, TypeVar, Optional
2
+ from pure_python_ds.linear.singly_linked_list import SinglyLinkedList
3
+
4
+ T = TypeVar('T')
5
+
6
+ class Queue(Generic[T]):
7
+ """A strictly typed, memory-optimized Queue (FIFO)."""
8
+
9
+ def __init__(self):
10
+ self._container = SinglyLinkedList[T]()
11
+
12
+ def __len__(self) -> int:
13
+ return len(self._container)
14
+
15
+ def enqueue(self, value: T) -> None:
16
+ """Adds an item to the back of the queue in O(1) time."""
17
+ self._container.append(value)
18
+
19
+ def dequeue(self) -> Optional[T]:
20
+ """Removes and returns the front item in O(1) time."""
21
+ return self._container.remove_head()
22
+
23
+ def __str__(self) -> str:
24
+ return f"Queue Front -> {self._container}"