exonware-xwnode 0.0.1.12__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.
- exonware/__init__.py +14 -0
- exonware/xwnode/__init__.py +127 -0
- exonware/xwnode/base.py +676 -0
- exonware/xwnode/config.py +178 -0
- exonware/xwnode/contracts.py +730 -0
- exonware/xwnode/errors.py +503 -0
- exonware/xwnode/facade.py +460 -0
- exonware/xwnode/strategies/__init__.py +158 -0
- exonware/xwnode/strategies/advisor.py +463 -0
- exonware/xwnode/strategies/edges/__init__.py +32 -0
- exonware/xwnode/strategies/edges/adj_list.py +227 -0
- exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
- exonware/xwnode/strategies/edges/base.py +169 -0
- exonware/xwnode/strategies/flyweight.py +328 -0
- exonware/xwnode/strategies/impls/__init__.py +13 -0
- exonware/xwnode/strategies/impls/_base_edge.py +403 -0
- exonware/xwnode/strategies/impls/_base_node.py +307 -0
- exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
- exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
- exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
- exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
- exonware/xwnode/strategies/impls/edge_coo.py +533 -0
- exonware/xwnode/strategies/impls/edge_csc.py +447 -0
- exonware/xwnode/strategies/impls/edge_csr.py +492 -0
- exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
- exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
- exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
- exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
- exonware/xwnode/strategies/impls/edge_octree.py +574 -0
- exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
- exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
- exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
- exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
- exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
- exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
- exonware/xwnode/strategies/manager.py +775 -0
- exonware/xwnode/strategies/metrics.py +538 -0
- exonware/xwnode/strategies/migration.py +432 -0
- exonware/xwnode/strategies/nodes/__init__.py +50 -0
- exonware/xwnode/strategies/nodes/_base_node.py +307 -0
- exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
- exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
- exonware/xwnode/strategies/nodes/array_list.py +209 -0
- exonware/xwnode/strategies/nodes/base.py +247 -0
- exonware/xwnode/strategies/nodes/deque.py +200 -0
- exonware/xwnode/strategies/nodes/hash_map.py +135 -0
- exonware/xwnode/strategies/nodes/heap.py +307 -0
- exonware/xwnode/strategies/nodes/linked_list.py +232 -0
- exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
- exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
- exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
- exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
- exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
- exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
- exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
- exonware/xwnode/strategies/nodes/node_btree.py +357 -0
- exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
- exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
- exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
- exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
- exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
- exonware/xwnode/strategies/nodes/node_heap.py +191 -0
- exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
- exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
- exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
- exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
- exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
- exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
- exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
- exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
- exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
- exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
- exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
- exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
- exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
- exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
- exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
- exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
- exonware/xwnode/strategies/nodes/node_treap.py +387 -0
- exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
- exonware/xwnode/strategies/nodes/node_trie.py +252 -0
- exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
- exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
- exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
- exonware/xwnode/strategies/nodes/queue.py +161 -0
- exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
- exonware/xwnode/strategies/nodes/stack.py +152 -0
- exonware/xwnode/strategies/nodes/trie.py +274 -0
- exonware/xwnode/strategies/nodes/union_find.py +283 -0
- exonware/xwnode/strategies/pattern_detector.py +603 -0
- exonware/xwnode/strategies/performance_monitor.py +487 -0
- exonware/xwnode/strategies/queries/__init__.py +24 -0
- exonware/xwnode/strategies/queries/base.py +236 -0
- exonware/xwnode/strategies/queries/cql.py +201 -0
- exonware/xwnode/strategies/queries/cypher.py +181 -0
- exonware/xwnode/strategies/queries/datalog.py +70 -0
- exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
- exonware/xwnode/strategies/queries/eql.py +70 -0
- exonware/xwnode/strategies/queries/flux.py +70 -0
- exonware/xwnode/strategies/queries/gql.py +70 -0
- exonware/xwnode/strategies/queries/graphql.py +240 -0
- exonware/xwnode/strategies/queries/gremlin.py +181 -0
- exonware/xwnode/strategies/queries/hiveql.py +214 -0
- exonware/xwnode/strategies/queries/hql.py +70 -0
- exonware/xwnode/strategies/queries/jmespath.py +219 -0
- exonware/xwnode/strategies/queries/jq.py +66 -0
- exonware/xwnode/strategies/queries/json_query.py +66 -0
- exonware/xwnode/strategies/queries/jsoniq.py +248 -0
- exonware/xwnode/strategies/queries/kql.py +70 -0
- exonware/xwnode/strategies/queries/linq.py +238 -0
- exonware/xwnode/strategies/queries/logql.py +70 -0
- exonware/xwnode/strategies/queries/mql.py +68 -0
- exonware/xwnode/strategies/queries/n1ql.py +210 -0
- exonware/xwnode/strategies/queries/partiql.py +70 -0
- exonware/xwnode/strategies/queries/pig.py +215 -0
- exonware/xwnode/strategies/queries/promql.py +70 -0
- exonware/xwnode/strategies/queries/sparql.py +220 -0
- exonware/xwnode/strategies/queries/sql.py +275 -0
- exonware/xwnode/strategies/queries/xml_query.py +66 -0
- exonware/xwnode/strategies/queries/xpath.py +223 -0
- exonware/xwnode/strategies/queries/xquery.py +258 -0
- exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
- exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
- exonware/xwnode/strategies/registry.py +604 -0
- exonware/xwnode/strategies/simple.py +273 -0
- exonware/xwnode/strategies/utils.py +532 -0
- exonware/xwnode/types.py +912 -0
- exonware/xwnode/version.py +78 -0
- exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
- exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
- exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
- exonware_xwnode-0.0.1.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,371 @@
|
|
1
|
+
#exonware\xnode\strategies\impls\node_avl_tree.py
|
2
|
+
"""
|
3
|
+
AVL Tree Node Strategy Implementation
|
4
|
+
|
5
|
+
This module implements the AVL_TREE strategy for strictly balanced binary
|
6
|
+
search trees with guaranteed O(log n) height and operations.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Any, Iterator, List, Dict, Optional, Tuple
|
10
|
+
from .base import ANodeTreeStrategy
|
11
|
+
from ...types import NodeMode, NodeTrait
|
12
|
+
|
13
|
+
|
14
|
+
class AVLTreeNode:
|
15
|
+
"""Node in the AVL tree."""
|
16
|
+
|
17
|
+
def __init__(self, key: str, value: Any = None, height: int = 1):
|
18
|
+
self.key = key
|
19
|
+
self.value = value
|
20
|
+
self.height = height
|
21
|
+
self.left: Optional['AVLTreeNode'] = None
|
22
|
+
self.right: Optional['AVLTreeNode'] = None
|
23
|
+
self._hash = None
|
24
|
+
|
25
|
+
def __hash__(self) -> int:
|
26
|
+
"""Cache hash for performance."""
|
27
|
+
if self._hash is None:
|
28
|
+
self._hash = hash((self.key, self.value, self.height))
|
29
|
+
return self._hash
|
30
|
+
|
31
|
+
def __eq__(self, other) -> bool:
|
32
|
+
"""Structural equality."""
|
33
|
+
if not isinstance(other, AVLTreeNode):
|
34
|
+
return False
|
35
|
+
return (self.key == other.key and
|
36
|
+
self.value == other.value and
|
37
|
+
self.height == other.height)
|
38
|
+
|
39
|
+
|
40
|
+
class AVLTreeStrategy(ANodeTreeStrategy):
|
41
|
+
"""
|
42
|
+
AVL tree node strategy for strictly balanced binary search trees.
|
43
|
+
|
44
|
+
Provides guaranteed O(log n) height and operations through height-based
|
45
|
+
balancing rules and rotations.
|
46
|
+
"""
|
47
|
+
|
48
|
+
def __init__(self, traits: NodeTrait = NodeTrait.NONE, **options):
|
49
|
+
"""Initialize the AVL tree strategy."""
|
50
|
+
super().__init__(NodeMode.AVL_TREE, traits, **options)
|
51
|
+
|
52
|
+
self.case_sensitive = options.get('case_sensitive', True)
|
53
|
+
|
54
|
+
# Core AVL tree
|
55
|
+
self._root: Optional[AVLTreeNode] = None
|
56
|
+
self._size = 0
|
57
|
+
|
58
|
+
# Statistics
|
59
|
+
self._total_insertions = 0
|
60
|
+
self._total_deletions = 0
|
61
|
+
self._total_rotations = 0
|
62
|
+
self._max_height = 0
|
63
|
+
|
64
|
+
def get_supported_traits(self) -> NodeTrait:
|
65
|
+
"""Get the traits supported by the AVL tree strategy."""
|
66
|
+
return (NodeTrait.ORDERED | NodeTrait.INDEXED)
|
67
|
+
|
68
|
+
def _normalize_key(self, key: str) -> str:
|
69
|
+
"""Normalize key based on case sensitivity."""
|
70
|
+
return key if self.case_sensitive else key.lower()
|
71
|
+
|
72
|
+
def _get_height(self, node: Optional[AVLTreeNode]) -> int:
|
73
|
+
"""Get height of node."""
|
74
|
+
return node.height if node else 0
|
75
|
+
|
76
|
+
def _get_balance(self, node: Optional[AVLTreeNode]) -> int:
|
77
|
+
"""Get balance factor of node."""
|
78
|
+
if not node:
|
79
|
+
return 0
|
80
|
+
return self._get_height(node.left) - self._get_height(node.right)
|
81
|
+
|
82
|
+
def _update_height(self, node: AVLTreeNode) -> None:
|
83
|
+
"""Update height of node."""
|
84
|
+
node.height = 1 + max(self._get_height(node.left), self._get_height(node.right))
|
85
|
+
|
86
|
+
def _rotate_right(self, node: AVLTreeNode) -> AVLTreeNode:
|
87
|
+
"""Right rotation around node."""
|
88
|
+
left_child = node.left
|
89
|
+
if not left_child:
|
90
|
+
return node
|
91
|
+
|
92
|
+
# Perform rotation
|
93
|
+
node.left = left_child.right
|
94
|
+
left_child.right = node
|
95
|
+
|
96
|
+
# Update heights
|
97
|
+
self._update_height(node)
|
98
|
+
self._update_height(left_child)
|
99
|
+
|
100
|
+
self._total_rotations += 1
|
101
|
+
return left_child
|
102
|
+
|
103
|
+
def _rotate_left(self, node: AVLTreeNode) -> AVLTreeNode:
|
104
|
+
"""Left rotation around node."""
|
105
|
+
right_child = node.right
|
106
|
+
if not right_child:
|
107
|
+
return node
|
108
|
+
|
109
|
+
# Perform rotation
|
110
|
+
node.right = right_child.left
|
111
|
+
right_child.left = node
|
112
|
+
|
113
|
+
# Update heights
|
114
|
+
self._update_height(node)
|
115
|
+
self._update_height(right_child)
|
116
|
+
|
117
|
+
self._total_rotations += 1
|
118
|
+
return right_child
|
119
|
+
|
120
|
+
def _balance_node(self, node: AVLTreeNode) -> AVLTreeNode:
|
121
|
+
"""Balance node using AVL rotations."""
|
122
|
+
# Update height
|
123
|
+
self._update_height(node)
|
124
|
+
|
125
|
+
# Get balance factor
|
126
|
+
balance = self._get_balance(node)
|
127
|
+
|
128
|
+
# Left heavy
|
129
|
+
if balance > 1:
|
130
|
+
if self._get_balance(node.left) < 0:
|
131
|
+
# Left-Right case
|
132
|
+
node.left = self._rotate_left(node.left)
|
133
|
+
return self._rotate_right(node)
|
134
|
+
else:
|
135
|
+
# Left-Left case
|
136
|
+
return self._rotate_right(node)
|
137
|
+
|
138
|
+
# Right heavy
|
139
|
+
if balance < -1:
|
140
|
+
if self._get_balance(node.right) > 0:
|
141
|
+
# Right-Left case
|
142
|
+
node.right = self._rotate_right(node.right)
|
143
|
+
return self._rotate_left(node)
|
144
|
+
else:
|
145
|
+
# Right-Right case
|
146
|
+
return self._rotate_left(node)
|
147
|
+
|
148
|
+
return node
|
149
|
+
|
150
|
+
def _insert_node(self, node: Optional[AVLTreeNode], key: str, value: Any) -> Tuple[AVLTreeNode, bool]:
|
151
|
+
"""Insert node with given key and value."""
|
152
|
+
if not node:
|
153
|
+
new_node = AVLTreeNode(key, value)
|
154
|
+
return new_node, True
|
155
|
+
|
156
|
+
normalized_key = self._normalize_key(key)
|
157
|
+
node_key = self._normalize_key(node.key)
|
158
|
+
|
159
|
+
if normalized_key < node_key:
|
160
|
+
node.left, inserted = self._insert_node(node.left, key, value)
|
161
|
+
elif normalized_key > node_key:
|
162
|
+
node.right, inserted = self._insert_node(node.right, key, value)
|
163
|
+
else:
|
164
|
+
# Key already exists, update value
|
165
|
+
node.value = value
|
166
|
+
return node, False
|
167
|
+
|
168
|
+
# Balance the node
|
169
|
+
balanced_node = self._balance_node(node)
|
170
|
+
return balanced_node, inserted
|
171
|
+
|
172
|
+
def _find_node(self, node: Optional[AVLTreeNode], key: str) -> Optional[AVLTreeNode]:
|
173
|
+
"""Find node with given key."""
|
174
|
+
if not node:
|
175
|
+
return None
|
176
|
+
|
177
|
+
normalized_key = self._normalize_key(key)
|
178
|
+
node_key = self._normalize_key(node.key)
|
179
|
+
|
180
|
+
if normalized_key < node_key:
|
181
|
+
return self._find_node(node.left, key)
|
182
|
+
elif normalized_key > node_key:
|
183
|
+
return self._find_node(node.right, key)
|
184
|
+
else:
|
185
|
+
return node
|
186
|
+
|
187
|
+
def _find_min(self, node: AVLTreeNode) -> AVLTreeNode:
|
188
|
+
"""Find minimum node in subtree."""
|
189
|
+
while node.left:
|
190
|
+
node = node.left
|
191
|
+
return node
|
192
|
+
|
193
|
+
def _find_max(self, node: AVLTreeNode) -> AVLTreeNode:
|
194
|
+
"""Find maximum node in subtree."""
|
195
|
+
while node.right:
|
196
|
+
node = node.right
|
197
|
+
return node
|
198
|
+
|
199
|
+
def _delete_node(self, node: Optional[AVLTreeNode], key: str) -> Tuple[Optional[AVLTreeNode], bool]:
|
200
|
+
"""Delete node with given key."""
|
201
|
+
if not node:
|
202
|
+
return None, False
|
203
|
+
|
204
|
+
normalized_key = self._normalize_key(key)
|
205
|
+
node_key = self._normalize_key(node.key)
|
206
|
+
|
207
|
+
if normalized_key < node_key:
|
208
|
+
node.left, deleted = self._delete_node(node.left, key)
|
209
|
+
elif normalized_key > node_key:
|
210
|
+
node.right, deleted = self._delete_node(node.right, key)
|
211
|
+
else:
|
212
|
+
# Found node to delete
|
213
|
+
if not node.left:
|
214
|
+
return node.right, True
|
215
|
+
elif not node.right:
|
216
|
+
return node.left, True
|
217
|
+
else:
|
218
|
+
# Node has both children, find successor
|
219
|
+
successor = self._find_min(node.right)
|
220
|
+
node.key = successor.key
|
221
|
+
node.value = successor.value
|
222
|
+
node.right, _ = self._delete_node(node.right, successor.key)
|
223
|
+
deleted = True
|
224
|
+
|
225
|
+
if not deleted:
|
226
|
+
return node, False
|
227
|
+
|
228
|
+
# Balance the node
|
229
|
+
balanced_node = self._balance_node(node)
|
230
|
+
return balanced_node, True
|
231
|
+
|
232
|
+
def _inorder_traversal(self, node: Optional[AVLTreeNode]) -> Iterator[Tuple[str, Any]]:
|
233
|
+
"""In-order traversal of tree."""
|
234
|
+
if node:
|
235
|
+
yield from self._inorder_traversal(node.left)
|
236
|
+
yield (node.key, node.value)
|
237
|
+
yield from self._inorder_traversal(node.right)
|
238
|
+
|
239
|
+
# ============================================================================
|
240
|
+
# CORE OPERATIONS
|
241
|
+
# ============================================================================
|
242
|
+
|
243
|
+
def put(self, key: Any, value: Any = None) -> None:
|
244
|
+
"""Store a key-value pair."""
|
245
|
+
if not isinstance(key, str):
|
246
|
+
key = str(key)
|
247
|
+
|
248
|
+
self._root, inserted = self._insert_node(self._root, key, value)
|
249
|
+
if inserted:
|
250
|
+
self._size += 1
|
251
|
+
self._total_insertions += 1
|
252
|
+
self._max_height = max(self._max_height, self._get_height(self._root))
|
253
|
+
|
254
|
+
def get(self, key: Any, default: Any = None) -> Any:
|
255
|
+
"""Retrieve a value by key."""
|
256
|
+
if not isinstance(key, str):
|
257
|
+
key = str(key)
|
258
|
+
|
259
|
+
node = self._find_node(self._root, key)
|
260
|
+
return node.value if node else default
|
261
|
+
|
262
|
+
def delete(self, key: Any) -> bool:
|
263
|
+
"""Remove a key-value pair."""
|
264
|
+
if not isinstance(key, str):
|
265
|
+
key = str(key)
|
266
|
+
|
267
|
+
self._root, deleted = self._delete_node(self._root, key)
|
268
|
+
if deleted:
|
269
|
+
self._size -= 1
|
270
|
+
self._total_deletions += 1
|
271
|
+
return deleted
|
272
|
+
|
273
|
+
def has(self, key: Any) -> bool:
|
274
|
+
"""Check if key exists."""
|
275
|
+
if not isinstance(key, str):
|
276
|
+
key = str(key)
|
277
|
+
|
278
|
+
return self._find_node(self._root, key) is not None
|
279
|
+
|
280
|
+
def clear(self) -> None:
|
281
|
+
"""Clear all data."""
|
282
|
+
self._root = None
|
283
|
+
self._size = 0
|
284
|
+
|
285
|
+
def size(self) -> int:
|
286
|
+
"""Get number of key-value pairs."""
|
287
|
+
return self._size
|
288
|
+
|
289
|
+
def is_empty(self) -> bool:
|
290
|
+
"""Check if tree is empty."""
|
291
|
+
return self._root is None
|
292
|
+
|
293
|
+
# ============================================================================
|
294
|
+
# ITERATION
|
295
|
+
# ============================================================================
|
296
|
+
|
297
|
+
def keys(self) -> Iterator[str]:
|
298
|
+
"""Iterate over keys in sorted order."""
|
299
|
+
for key, _ in self._inorder_traversal(self._root):
|
300
|
+
yield key
|
301
|
+
|
302
|
+
def values(self) -> Iterator[Any]:
|
303
|
+
"""Iterate over values in key order."""
|
304
|
+
for _, value in self._inorder_traversal(self._root):
|
305
|
+
yield value
|
306
|
+
|
307
|
+
def items(self) -> Iterator[Tuple[str, Any]]:
|
308
|
+
"""Iterate over key-value pairs in sorted order."""
|
309
|
+
yield from self._inorder_traversal(self._root)
|
310
|
+
|
311
|
+
def __iter__(self) -> Iterator[str]:
|
312
|
+
"""Iterate over keys."""
|
313
|
+
yield from self.keys()
|
314
|
+
|
315
|
+
# ============================================================================
|
316
|
+
# AVL TREE SPECIFIC OPERATIONS
|
317
|
+
# ============================================================================
|
318
|
+
|
319
|
+
def get_min(self) -> Optional[Tuple[str, Any]]:
|
320
|
+
"""Get the minimum key-value pair."""
|
321
|
+
if not self._root:
|
322
|
+
return None
|
323
|
+
|
324
|
+
min_node = self._find_min(self._root)
|
325
|
+
return (min_node.key, min_node.value)
|
326
|
+
|
327
|
+
def get_max(self) -> Optional[Tuple[str, Any]]:
|
328
|
+
"""Get the maximum key-value pair."""
|
329
|
+
if not self._root:
|
330
|
+
return None
|
331
|
+
|
332
|
+
max_node = self._find_max(self._root)
|
333
|
+
return (max_node.key, max_node.value)
|
334
|
+
|
335
|
+
def get_height(self) -> int:
|
336
|
+
"""Get the height of the tree."""
|
337
|
+
return self._get_height(self._root)
|
338
|
+
|
339
|
+
def is_balanced(self) -> bool:
|
340
|
+
"""Check if tree is AVL balanced."""
|
341
|
+
def check_balance(node: Optional[AVLTreeNode]) -> bool:
|
342
|
+
if not node:
|
343
|
+
return True
|
344
|
+
|
345
|
+
balance = self._get_balance(node)
|
346
|
+
if abs(balance) > 1:
|
347
|
+
return False
|
348
|
+
|
349
|
+
return check_balance(node.left) and check_balance(node.right)
|
350
|
+
|
351
|
+
return check_balance(self._root)
|
352
|
+
|
353
|
+
def get_balance_factor(self, key: str) -> Optional[int]:
|
354
|
+
"""Get balance factor of node with given key."""
|
355
|
+
node = self._find_node(self._root, key)
|
356
|
+
return self._get_balance(node) if node else None
|
357
|
+
|
358
|
+
def get_stats(self) -> Dict[str, Any]:
|
359
|
+
"""Get performance statistics."""
|
360
|
+
return {
|
361
|
+
'size': self._size,
|
362
|
+
'height': self._get_height(self._root),
|
363
|
+
'max_height': self._max_height,
|
364
|
+
'total_insertions': self._total_insertions,
|
365
|
+
'total_deletions': self._total_deletions,
|
366
|
+
'total_rotations': self._total_rotations,
|
367
|
+
'is_balanced': self.is_balanced(),
|
368
|
+
'strategy': 'AVL_TREE',
|
369
|
+
'backend': 'Strictly balanced AVL tree with guaranteed O(log n) height',
|
370
|
+
'traits': [trait.name for trait in NodeTrait if self.has_trait(trait)]
|
371
|
+
}
|