pkstruct 0.1.0__py3-none-any.whl
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.
- pkstruct/__init__.py +167 -0
- pkstruct/graphs/__init__.py +127 -0
- pkstruct/graphs/connectivity.py +157 -0
- pkstruct/graphs/directed.py +95 -0
- pkstruct/graphs/exceptions.py +63 -0
- pkstruct/graphs/graph.py +262 -0
- pkstruct/graphs/mst.py +118 -0
- pkstruct/graphs/scc.py +138 -0
- pkstruct/graphs/shortest_path.py +250 -0
- pkstruct/graphs/topo_sort.py +108 -0
- pkstruct/graphs/traversal.py +175 -0
- pkstruct/graphs/visualization.py +90 -0
- pkstruct/graphs/weighted.py +37 -0
- pkstruct/linear/__init__.py +95 -0
- pkstruct/linear/deques/__init__.py +33 -0
- pkstruct/linear/deques/deque.py +194 -0
- pkstruct/linear/deques/linked_deque.py +198 -0
- pkstruct/linear/exceptions.py +26 -0
- pkstruct/linear/linked_lists/__init__.py +5 -0
- pkstruct/linear/linked_lists/_base.py +608 -0
- pkstruct/linear/linked_lists/circular_linked_list.py +230 -0
- pkstruct/linear/linked_lists/doubly_linked_list.py +151 -0
- pkstruct/linear/linked_lists/nodes.py +68 -0
- pkstruct/linear/linked_lists/singly_linked_list.py +136 -0
- pkstruct/linear/queues/__init__.py +44 -0
- pkstruct/linear/queues/circular_queue.py +258 -0
- pkstruct/linear/queues/linked_queue.py +186 -0
- pkstruct/linear/queues/priority_queue.py +202 -0
- pkstruct/linear/queues/queue.py +174 -0
- pkstruct/linear/stacks/__init__.py +38 -0
- pkstruct/linear/stacks/array_stack.py +165 -0
- pkstruct/linear/stacks/linked_stack.py +168 -0
- pkstruct/linear/stacks/stack.py +158 -0
- pkstruct/linear/utils/__init__.py +18 -0
- pkstruct/linear/utils/benchmark.py +255 -0
- pkstruct/linear/utils/debug_tools.py +239 -0
- pkstruct/linear/utils/helpers.py +143 -0
- pkstruct/linear/utils/iterators.py +148 -0
- pkstruct/linear/visualization/__init__.py +0 -0
- pkstruct/linear/visualization/ascii_visualizer.py +114 -0
- pkstruct/linear/visualization/linked_list_visualizer.py +126 -0
- pkstruct/shared/__init__.py +67 -0
- pkstruct/shared/benchmarking/__init__.py +78 -0
- pkstruct/shared/debugging/__init__.py +69 -0
- pkstruct/shared/exceptions/__init__.py +59 -0
- pkstruct/shared/serializers/__init__.py +65 -0
- pkstruct/shared/threading/__init__.py +43 -0
- pkstruct/shared/validators/__init__.py +98 -0
- pkstruct/shared/visualization/__init__.py +21 -0
- pkstruct/trees/__init__.py +92 -0
- pkstruct/trees/avl.py +321 -0
- pkstruct/trees/balancing.py +253 -0
- pkstruct/trees/bplus.py +425 -0
- pkstruct/trees/bst.py +948 -0
- pkstruct/trees/btree.py +504 -0
- pkstruct/trees/exceptions.py +96 -0
- pkstruct/trees/fenwick_tree.py +312 -0
- pkstruct/trees/interval_tree.py +541 -0
- pkstruct/trees/node.py +356 -0
- pkstruct/trees/red_black.py +710 -0
- pkstruct/trees/segment_tree.py +398 -0
- pkstruct/trees/traversal.py +456 -0
- pkstruct/trees/tree_helpers.py +366 -0
- pkstruct/trees/utils/__init__.py +15 -0
- pkstruct/trees/utils/complexity_helpers.py +231 -0
- pkstruct/trees/visualization/__init__.py +0 -0
- pkstruct/trees/visualization/ascii_renderer.py +220 -0
- pkstruct/trees/visualization/tree_printer.py +129 -0
- pkstruct-0.1.0.dist-info/METADATA +482 -0
- pkstruct-0.1.0.dist-info/RECORD +72 -0
- pkstruct-0.1.0.dist-info/WHEEL +4 -0
- pkstruct-0.1.0.dist-info/licenses/LICENSE +21 -0
pkstruct/trees/node.py
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pkstruct.trees.node
|
|
3
|
+
===================
|
|
4
|
+
Reusable node classes for all tree implementations.
|
|
5
|
+
|
|
6
|
+
Inheritance hierarchy::
|
|
7
|
+
|
|
8
|
+
TreeNode
|
|
9
|
+
├── AVLNode (adds height)
|
|
10
|
+
├── RBNode (adds color)
|
|
11
|
+
├── SegmentNode (adds segment metadata)
|
|
12
|
+
└── IntervalNode (adds interval + max_endpoint)
|
|
13
|
+
|
|
14
|
+
BTreeNode (multi-key / multi-child; standalone)
|
|
15
|
+
BPlusNode (leaf-linked variant of BTreeNode)
|
|
16
|
+
|
|
17
|
+
Every node exposes only the fields it needs; fields not relevant to a
|
|
18
|
+
concrete subclass are never allocated.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from typing import Any, Generic, TypeVar
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"TreeNode",
|
|
27
|
+
"AVLNode",
|
|
28
|
+
"RBNode",
|
|
29
|
+
"BTreeNode",
|
|
30
|
+
"BPlusNode",
|
|
31
|
+
"SegmentNode",
|
|
32
|
+
"IntervalNode",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
KT = TypeVar("KT") # key type
|
|
36
|
+
VT = TypeVar("VT") # value type
|
|
37
|
+
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
# Sentinel colours used by RBNode (module-level so they are importable)
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
RED: int = 0
|
|
42
|
+
BLACK: int = 1
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TreeNode(Generic[KT, VT]):
|
|
46
|
+
"""
|
|
47
|
+
Base binary-tree node.
|
|
48
|
+
|
|
49
|
+
Attributes
|
|
50
|
+
----------
|
|
51
|
+
key:
|
|
52
|
+
Comparable key used for ordering.
|
|
53
|
+
value:
|
|
54
|
+
Arbitrary payload; defaults to *None*.
|
|
55
|
+
left:
|
|
56
|
+
Left child or *None*.
|
|
57
|
+
right:
|
|
58
|
+
Right child or *None*.
|
|
59
|
+
parent:
|
|
60
|
+
Parent node or *None* (root).
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
__slots__ = ("key", "value", "left", "right", "parent")
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
key: KT,
|
|
68
|
+
value: VT | None = None,
|
|
69
|
+
*,
|
|
70
|
+
left: TreeNode[KT, VT] | None = None,
|
|
71
|
+
right: TreeNode[KT, VT] | None = None,
|
|
72
|
+
parent: TreeNode[KT, VT] | None = None,
|
|
73
|
+
) -> None:
|
|
74
|
+
self.key: KT = key
|
|
75
|
+
self.value: VT | None = value
|
|
76
|
+
self.left: TreeNode[KT, VT] | None = left
|
|
77
|
+
self.right: TreeNode[KT, VT] | None = right
|
|
78
|
+
self.parent: TreeNode[KT, VT] | None = parent
|
|
79
|
+
|
|
80
|
+
# ------------------------------------------------------------------
|
|
81
|
+
# Helpers
|
|
82
|
+
# ------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
def is_leaf(self) -> bool:
|
|
85
|
+
"""Return *True* when the node has no children."""
|
|
86
|
+
return self.left is None and self.right is None
|
|
87
|
+
|
|
88
|
+
def is_root(self) -> bool:
|
|
89
|
+
"""Return *True* when the node has no parent."""
|
|
90
|
+
return self.parent is None
|
|
91
|
+
|
|
92
|
+
def is_left_child(self) -> bool:
|
|
93
|
+
"""Return *True* when this node is the left child of its parent."""
|
|
94
|
+
return self.parent is not None and self.parent.left is self
|
|
95
|
+
|
|
96
|
+
def is_right_child(self) -> bool:
|
|
97
|
+
"""Return *True* when this node is the right child of its parent."""
|
|
98
|
+
return self.parent is not None and self.parent.right is self
|
|
99
|
+
|
|
100
|
+
def sibling(self) -> TreeNode[KT, VT] | None:
|
|
101
|
+
"""Return the sibling node, or *None* if no parent."""
|
|
102
|
+
if self.parent is None:
|
|
103
|
+
return None
|
|
104
|
+
return self.parent.right if self.is_left_child() else self.parent.left
|
|
105
|
+
|
|
106
|
+
def __repr__(self) -> str: # pragma: no cover
|
|
107
|
+
return f"{type(self).__name__}(key={self.key!r}, value={self.value!r})"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# AVL Node
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class AVLNode(TreeNode[KT, VT]):
|
|
116
|
+
"""
|
|
117
|
+
AVL-tree node with *height* field.
|
|
118
|
+
|
|
119
|
+
The height of a leaf is **1**; *None* sentinel nodes have height **0**.
|
|
120
|
+
|
|
121
|
+
Attributes
|
|
122
|
+
----------
|
|
123
|
+
height:
|
|
124
|
+
Height of the sub-tree rooted at this node.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
__slots__ = ("height",)
|
|
128
|
+
|
|
129
|
+
def __init__(
|
|
130
|
+
self,
|
|
131
|
+
key: KT,
|
|
132
|
+
value: VT | None = None,
|
|
133
|
+
*,
|
|
134
|
+
left: AVLNode[KT, VT] | None = None,
|
|
135
|
+
right: AVLNode[KT, VT] | None = None,
|
|
136
|
+
parent: AVLNode[KT, VT] | None = None,
|
|
137
|
+
) -> None:
|
|
138
|
+
super().__init__(key, value, left=left, right=right, parent=parent)
|
|
139
|
+
self.height: int = 1
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# ---------------------------------------------------------------------------
|
|
143
|
+
# Red-Black Node
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class RBNode(TreeNode[KT, VT]):
|
|
148
|
+
"""
|
|
149
|
+
Red-black tree node with *color* field.
|
|
150
|
+
|
|
151
|
+
Attributes
|
|
152
|
+
----------
|
|
153
|
+
color:
|
|
154
|
+
Either :data:`RED` (0) or :data:`BLACK` (1).
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
__slots__ = ("color",)
|
|
158
|
+
|
|
159
|
+
def __init__(
|
|
160
|
+
self,
|
|
161
|
+
key: KT,
|
|
162
|
+
value: VT | None = None,
|
|
163
|
+
*,
|
|
164
|
+
color: int = RED,
|
|
165
|
+
left: RBNode[KT, VT] | None = None,
|
|
166
|
+
right: RBNode[KT, VT] | None = None,
|
|
167
|
+
parent: RBNode[KT, VT] | None = None,
|
|
168
|
+
) -> None:
|
|
169
|
+
super().__init__(key, value, left=left, right=right, parent=parent)
|
|
170
|
+
self.color: int = color
|
|
171
|
+
|
|
172
|
+
def is_red(self) -> bool:
|
|
173
|
+
"""Return *True* when the node is red."""
|
|
174
|
+
return self.color == RED
|
|
175
|
+
|
|
176
|
+
def is_black(self) -> bool:
|
|
177
|
+
"""Return *True* when the node is black."""
|
|
178
|
+
return self.color == BLACK
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# ---------------------------------------------------------------------------
|
|
182
|
+
# B-Tree Node
|
|
183
|
+
# ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class BTreeNode(Generic[KT, VT]):
|
|
187
|
+
"""
|
|
188
|
+
Multi-key / multi-child node used by :class:`~pkstruct.trees.btree.BTree`.
|
|
189
|
+
|
|
190
|
+
Attributes
|
|
191
|
+
----------
|
|
192
|
+
keys:
|
|
193
|
+
Ordered list of keys stored in this node.
|
|
194
|
+
values:
|
|
195
|
+
Parallel list of values; ``values[i]`` corresponds to ``keys[i]``.
|
|
196
|
+
children:
|
|
197
|
+
Child nodes; an internal node with *k* keys has *k+1* children.
|
|
198
|
+
Leaf nodes have an empty children list.
|
|
199
|
+
parent:
|
|
200
|
+
Parent node or *None* (root).
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
__slots__ = ("keys", "values", "children", "parent")
|
|
204
|
+
|
|
205
|
+
def __init__(self) -> None:
|
|
206
|
+
self.keys: list[KT] = []
|
|
207
|
+
self.values: list[VT | None] = []
|
|
208
|
+
self.children: list[BTreeNode[KT, VT]] = []
|
|
209
|
+
self.parent: BTreeNode[KT, VT] | None = None
|
|
210
|
+
|
|
211
|
+
def is_leaf(self) -> bool:
|
|
212
|
+
"""Return *True* when this node has no children."""
|
|
213
|
+
return len(self.children) == 0
|
|
214
|
+
|
|
215
|
+
def __repr__(self) -> str: # pragma: no cover
|
|
216
|
+
return f"BTreeNode(keys={self.keys!r})"
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# ---------------------------------------------------------------------------
|
|
220
|
+
# B+ Tree Node
|
|
221
|
+
# ---------------------------------------------------------------------------
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class BPlusNode(Generic[KT, VT]):
|
|
225
|
+
"""
|
|
226
|
+
Node for :class:`~pkstruct.trees.bplus.BPlusTree`.
|
|
227
|
+
|
|
228
|
+
Leaf nodes carry ``(key, value)`` pairs and a ``next_leaf`` pointer.
|
|
229
|
+
Internal nodes carry separator keys and child pointers.
|
|
230
|
+
|
|
231
|
+
Attributes
|
|
232
|
+
----------
|
|
233
|
+
keys:
|
|
234
|
+
Ordered list of keys.
|
|
235
|
+
values:
|
|
236
|
+
Values stored only in leaf nodes.
|
|
237
|
+
children:
|
|
238
|
+
Child pointers (internal nodes only).
|
|
239
|
+
parent:
|
|
240
|
+
Parent node or *None*.
|
|
241
|
+
next_leaf:
|
|
242
|
+
Pointer to the next leaf (leaf nodes only); forms a linked list.
|
|
243
|
+
is_leaf:
|
|
244
|
+
*True* when this is a leaf node.
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
__slots__ = ("keys", "values", "children", "parent", "next_leaf", "is_leaf")
|
|
248
|
+
|
|
249
|
+
def __init__(self, *, is_leaf: bool = False) -> None:
|
|
250
|
+
self.keys: list[KT] = []
|
|
251
|
+
self.values: list[VT | None] = [] # leaf only
|
|
252
|
+
self.children: list[BPlusNode[KT, VT]] = [] # internal only
|
|
253
|
+
self.parent: BPlusNode[KT, VT] | None = None
|
|
254
|
+
self.next_leaf: BPlusNode[KT, VT] | None = None # leaf only
|
|
255
|
+
self.is_leaf: bool = is_leaf
|
|
256
|
+
|
|
257
|
+
def __repr__(self) -> str: # pragma: no cover
|
|
258
|
+
return f"BPlusNode(keys={self.keys!r}, leaf={self.is_leaf})"
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# ---------------------------------------------------------------------------
|
|
262
|
+
# Segment Tree Node
|
|
263
|
+
# ---------------------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class SegmentNode:
|
|
267
|
+
"""
|
|
268
|
+
Node for :class:`~pkstruct.trees.segment_tree.SegmentTree`.
|
|
269
|
+
|
|
270
|
+
The tree is stored as a flat array (1-indexed), so this class is used
|
|
271
|
+
only when a pointer-based implementation is preferred.
|
|
272
|
+
|
|
273
|
+
Attributes
|
|
274
|
+
----------
|
|
275
|
+
start:
|
|
276
|
+
Left boundary of the segment (inclusive).
|
|
277
|
+
end:
|
|
278
|
+
Right boundary of the segment (inclusive).
|
|
279
|
+
value:
|
|
280
|
+
Aggregated value (sum / min / max / …) of the segment.
|
|
281
|
+
lazy:
|
|
282
|
+
Pending lazy-propagation value; *None* means no pending update.
|
|
283
|
+
left:
|
|
284
|
+
Left child segment node.
|
|
285
|
+
right:
|
|
286
|
+
Right child segment node.
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
__slots__ = ("start", "end", "value", "lazy", "left", "right")
|
|
290
|
+
|
|
291
|
+
def __init__(
|
|
292
|
+
self,
|
|
293
|
+
start: int,
|
|
294
|
+
end: int,
|
|
295
|
+
value: Any = 0,
|
|
296
|
+
lazy: Any | None = None,
|
|
297
|
+
) -> None:
|
|
298
|
+
self.start: int = start
|
|
299
|
+
self.end: int = end
|
|
300
|
+
self.value: Any = value
|
|
301
|
+
self.lazy: Any | None = lazy
|
|
302
|
+
self.left: SegmentNode | None = None
|
|
303
|
+
self.right: SegmentNode | None = None
|
|
304
|
+
|
|
305
|
+
def __repr__(self) -> str: # pragma: no cover
|
|
306
|
+
return (
|
|
307
|
+
f"SegmentNode([{self.start},{self.end}], "
|
|
308
|
+
f"value={self.value!r}, lazy={self.lazy!r})"
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# ---------------------------------------------------------------------------
|
|
313
|
+
# Interval Tree Node
|
|
314
|
+
# ---------------------------------------------------------------------------
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class IntervalNode(TreeNode[KT, VT]):
|
|
318
|
+
"""
|
|
319
|
+
Augmented BST node used by :class:`~pkstruct.trees.interval_tree.IntervalTree`.
|
|
320
|
+
|
|
321
|
+
Each node represents a half-open or closed interval ``[lo, hi]``.
|
|
322
|
+
The ``max_endpoint`` field stores the maximum ``hi`` value in the
|
|
323
|
+
sub-tree rooted at this node, enabling O(log n) overlap queries.
|
|
324
|
+
|
|
325
|
+
Attributes
|
|
326
|
+
----------
|
|
327
|
+
lo:
|
|
328
|
+
Left endpoint of the interval (used as the BST key).
|
|
329
|
+
hi:
|
|
330
|
+
Right endpoint of the interval.
|
|
331
|
+
max_endpoint:
|
|
332
|
+
Maximum ``hi`` value in the sub-tree rooted here.
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
__slots__ = ("lo", "hi", "max_endpoint")
|
|
336
|
+
|
|
337
|
+
def __init__(
|
|
338
|
+
self,
|
|
339
|
+
lo: KT,
|
|
340
|
+
hi: KT,
|
|
341
|
+
value: VT | None = None,
|
|
342
|
+
*,
|
|
343
|
+
left: IntervalNode[KT, VT] | None = None,
|
|
344
|
+
right: IntervalNode[KT, VT] | None = None,
|
|
345
|
+
parent: IntervalNode[KT, VT] | None = None,
|
|
346
|
+
) -> None:
|
|
347
|
+
super().__init__(lo, value, left=left, right=right, parent=parent)
|
|
348
|
+
self.lo: KT = lo
|
|
349
|
+
self.hi: KT = hi
|
|
350
|
+
self.max_endpoint: KT = hi
|
|
351
|
+
|
|
352
|
+
def __repr__(self) -> str: # pragma: no cover
|
|
353
|
+
return (
|
|
354
|
+
f"IntervalNode([{self.lo!r}, {self.hi!r}], "
|
|
355
|
+
f"max={self.max_endpoint!r})"
|
|
356
|
+
)
|