stepps 0.2.0__tar.gz → 0.3.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 (55) hide show
  1. {stepps-0.2.0 → stepps-0.3.0}/PKG-INFO +1 -1
  2. {stepps-0.2.0 → stepps-0.3.0}/pyproject.toml +1 -1
  3. stepps-0.3.0/src/stepps/tree_builder/__init__.py +21 -0
  4. stepps-0.3.0/src/stepps/tree_builder/inorder_tree_builder.py +10 -0
  5. {stepps-0.2.0/src/stepps/TreeBuilder → stepps-0.3.0/src/stepps/tree_builder}/inorder_tree_builder_impl.py +1 -1
  6. stepps-0.3.0/src/stepps/tree_builder/level_order_tree_builder.py +10 -0
  7. {stepps-0.2.0/src/stepps/TreeBuilder → stepps-0.3.0/src/stepps/tree_builder}/level_order_tree_builder_impl.py +1 -1
  8. stepps-0.3.0/src/stepps/tree_builder/postorder_tree_builder.py +10 -0
  9. {stepps-0.2.0/src/stepps/TreeBuilder → stepps-0.3.0/src/stepps/tree_builder}/postorder_tree_builder_impl.py +1 -1
  10. stepps-0.3.0/src/stepps/tree_builder/preorder_tree_builder.py +10 -0
  11. {stepps-0.2.0/src/stepps/TreeBuilder → stepps-0.3.0/src/stepps/tree_builder}/preorder_tree_builder_impl.py +1 -1
  12. {stepps-0.2.0/src/stepps/TreeBuilder → stepps-0.3.0/src/stepps/tree_builder}/tree_builder.py +3 -2
  13. stepps-0.3.0/src/stepps/trees/balanced/__init__.py +4 -0
  14. stepps-0.3.0/src/stepps/trees/balanced/avl.py +19 -0
  15. stepps-0.3.0/src/stepps/trees/balanced/avl_impl.py +245 -0
  16. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/trees/binary_tree.py +26 -5
  17. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/trees/binary_tree_impl.py +24 -0
  18. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/trees/bst.py +1 -1
  19. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/trees/tree.py +3 -2
  20. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/visualizer/cli_visualizer.py +2 -1
  21. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/visualizer/tree_visualizer.py +3 -2
  22. {stepps-0.2.0 → stepps-0.3.0}/src/stepps.egg-info/PKG-INFO +1 -1
  23. {stepps-0.2.0 → stepps-0.3.0}/src/stepps.egg-info/SOURCES.txt +14 -10
  24. stepps-0.3.0/tests/test_avl.py +368 -0
  25. {stepps-0.2.0 → stepps-0.3.0}/tests/test_binary_tree.py +75 -0
  26. {stepps-0.2.0 → stepps-0.3.0}/tests/test_inorder_tree_builder.py +1 -1
  27. {stepps-0.2.0 → stepps-0.3.0}/tests/test_level_order_tree_builder.py +1 -1
  28. {stepps-0.2.0 → stepps-0.3.0}/tests/test_postorder_tree_builder.py +1 -1
  29. {stepps-0.2.0 → stepps-0.3.0}/tests/test_preorder_tree_builder.py +1 -1
  30. stepps-0.2.0/src/stepps/TreeBuilder/__init__.py +0 -13
  31. stepps-0.2.0/src/stepps/TreeBuilder/inorder_tree_builder.py +0 -8
  32. stepps-0.2.0/src/stepps/TreeBuilder/level_order_tree_builder.py +0 -8
  33. stepps-0.2.0/src/stepps/TreeBuilder/postorder_tree_builder.py +0 -8
  34. stepps-0.2.0/src/stepps/TreeBuilder/preorder_tree_builder.py +0 -8
  35. {stepps-0.2.0 → stepps-0.3.0}/LICENSE +0 -0
  36. {stepps-0.2.0 → stepps-0.3.0}/README.md +0 -0
  37. {stepps-0.2.0 → stepps-0.3.0}/setup.cfg +0 -0
  38. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/__init__.py +0 -0
  39. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/iterators/__init__.py +0 -0
  40. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/iterators/base_iterator.py +0 -0
  41. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/iterators/inorder.py +0 -0
  42. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/iterators/levelorder.py +0 -0
  43. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/iterators/postorder.py +0 -0
  44. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/iterators/preorder.py +0 -0
  45. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/nodes/__init__.py +0 -0
  46. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/nodes/binary_node.py +0 -0
  47. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/py.typed +0 -0
  48. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/trees/__init__.py +0 -0
  49. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/trees/bst_impl.py +0 -0
  50. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/visualizer/__init__.py +0 -0
  51. {stepps-0.2.0 → stepps-0.3.0}/src/stepps/visualizer/cli_visualizer_impl.py +0 -0
  52. {stepps-0.2.0 → stepps-0.3.0}/src/stepps.egg-info/dependency_links.txt +0 -0
  53. {stepps-0.2.0 → stepps-0.3.0}/src/stepps.egg-info/top_level.txt +0 -0
  54. {stepps-0.2.0 → stepps-0.3.0}/tests/test_bst.py +0 -0
  55. {stepps-0.2.0 → stepps-0.3.0}/tests/test_visualizer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stepps
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Useful algorithms in Python
5
5
  Author-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>, Trinay Mitra <trinay.mitra@vaastav.tech>
6
6
  Maintainer-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stepps"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  description = "Useful algorithms in Python"
5
5
  requires-python = ">=3.12"
6
6
  readme = "README.md"
@@ -0,0 +1,21 @@
1
+ from stepps.tree_builder.inorder_tree_builder import InOrderTreeBuilder
2
+ from stepps.tree_builder.inorder_tree_builder_impl import InOrderTreeBuilderImpl
3
+ from stepps.tree_builder.level_order_tree_builder import LevelOrderTreeBuilder
4
+ from stepps.tree_builder.level_order_tree_builder_impl import LevelOrderTreeBuilderImpl
5
+ from stepps.tree_builder.postorder_tree_builder import PostOrderTreeBuilder
6
+ from stepps.tree_builder.postorder_tree_builder_impl import PostOrderTreeBuilderImpl
7
+ from stepps.tree_builder.preorder_tree_builder import PreOrderTreeBuilder
8
+ from stepps.tree_builder.preorder_tree_builder_impl import PreOrderTreeBuilderImpl
9
+ from stepps.tree_builder.tree_builder import TreeBuilder
10
+
11
+ __all__ = [
12
+ "InOrderTreeBuilder",
13
+ "InOrderTreeBuilderImpl",
14
+ "LevelOrderTreeBuilder",
15
+ "LevelOrderTreeBuilderImpl",
16
+ "PostOrderTreeBuilder",
17
+ "PostOrderTreeBuilderImpl",
18
+ "PreOrderTreeBuilder",
19
+ "PreOrderTreeBuilderImpl",
20
+ "TreeBuilder",
21
+ ]
@@ -0,0 +1,10 @@
1
+ from typing import Protocol
2
+
3
+ from stepps.tree_builder.tree_builder import TreeBuilder
4
+
5
+
6
+ class InOrderTreeBuilder[T](TreeBuilder[T], Protocol):
7
+ """
8
+ Define the interface for building a binary tree from a sequence
9
+ using inorder traversal.
10
+ """
@@ -2,7 +2,7 @@ from collections.abc import Sequence
2
2
  from typing import override
3
3
 
4
4
  from stepps.nodes import BinaryNode
5
- from stepps.TreeBuilder.inorder_tree_builder import InOrderTreeBuilder
5
+ from stepps.tree_builder.inorder_tree_builder import InOrderTreeBuilder
6
6
 
7
7
 
8
8
  class InOrderTreeBuilderImpl[T](InOrderTreeBuilder[T]):
@@ -0,0 +1,10 @@
1
+ from typing import Protocol
2
+
3
+ from stepps.tree_builder.tree_builder import TreeBuilder
4
+
5
+
6
+ class LevelOrderTreeBuilder[T](TreeBuilder[T], Protocol):
7
+ """
8
+ Define the interface for building a binary tree from a sequence
9
+ using level-order traversal.
10
+ """
@@ -3,7 +3,7 @@ from collections.abc import Sequence
3
3
  from typing import override
4
4
 
5
5
  from stepps.nodes import BinaryNode
6
- from stepps.TreeBuilder.level_order_tree_builder import LevelOrderTreeBuilder
6
+ from stepps.tree_builder.level_order_tree_builder import LevelOrderTreeBuilder
7
7
 
8
8
 
9
9
  class LevelOrderTreeBuilderImpl[T](LevelOrderTreeBuilder[T]):
@@ -0,0 +1,10 @@
1
+ from typing import Protocol
2
+
3
+ from stepps.tree_builder.tree_builder import TreeBuilder
4
+
5
+
6
+ class PostOrderTreeBuilder[T](TreeBuilder[T], Protocol):
7
+ """
8
+ Define the interface for building a binary tree from a sequence
9
+ using postorder traversal.
10
+ """
@@ -2,7 +2,7 @@ from collections.abc import Sequence
2
2
  from typing import override
3
3
 
4
4
  from stepps.nodes import BinaryNode
5
- from stepps.TreeBuilder.postorder_tree_builder import PostOrderTreeBuilder
5
+ from stepps.tree_builder.postorder_tree_builder import PostOrderTreeBuilder
6
6
 
7
7
 
8
8
  class PostOrderTreeBuilderImpl[T](PostOrderTreeBuilder[T]):
@@ -0,0 +1,10 @@
1
+ from typing import Protocol
2
+
3
+ from stepps.tree_builder.tree_builder import TreeBuilder
4
+
5
+
6
+ class PreOrderTreeBuilder[T](TreeBuilder[T], Protocol):
7
+ """
8
+ Define the interface for building a binary tree from a sequence
9
+ using preorder traversal.
10
+ """
@@ -2,7 +2,7 @@ from collections.abc import Sequence
2
2
  from typing import override
3
3
 
4
4
  from stepps.nodes import BinaryNode
5
- from stepps.TreeBuilder.preorder_tree_builder import PreOrderTreeBuilder
5
+ from stepps.tree_builder.preorder_tree_builder import PreOrderTreeBuilder
6
6
 
7
7
 
8
8
  class PreOrderTreeBuilderImpl[T](PreOrderTreeBuilder[T]):
@@ -1,10 +1,11 @@
1
- from abc import ABC, abstractmethod
1
+ from abc import abstractmethod
2
2
  from collections.abc import Sequence
3
+ from typing import Protocol
3
4
 
4
5
  from stepps.nodes import BinaryNode
5
6
 
6
7
 
7
- class TreeBuilder[T](ABC):
8
+ class TreeBuilder[T](Protocol):
8
9
  """
9
10
  Define the interface for building a binary tree from a sequence.
10
11
  """
@@ -0,0 +1,4 @@
1
+ from stepps.trees.balanced.avl import AVL
2
+ from stepps.trees.balanced.avl_impl import AVLImpl
3
+
4
+ __all__ = ["AVL", "AVLImpl"]
@@ -0,0 +1,19 @@
1
+ from abc import abstractmethod
2
+ from typing import Protocol
3
+
4
+ from stepps.trees.binary_tree import BinaryTree
5
+
6
+
7
+ class AVL[T](BinaryTree[T], Protocol):
8
+ """
9
+ Define the interface for balancing a binary tree.
10
+ """
11
+
12
+ @abstractmethod
13
+ def balance(self) -> BinaryTree[T]:
14
+ """
15
+ Balance the binary tree.
16
+
17
+ :return: The balanced binary tree.
18
+ """
19
+ ...
@@ -0,0 +1,245 @@
1
+ from typing import override
2
+
3
+ from stepps.nodes import BinaryNode
4
+ from stepps.trees.balanced.avl import AVL
5
+ from stepps.trees.binary_tree import BinaryTree
6
+
7
+
8
+ class AVLImpl[T](AVL[T]):
9
+ """
10
+ Provide the default implementation for balancing a binary tree using AVL
11
+ rotations.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ tree: BinaryTree[T],
17
+ balance_factor: int = 1,
18
+ ) -> None:
19
+ self.tree = tree
20
+ self.balance_factor = balance_factor
21
+
22
+ @override
23
+ def balance(self) -> BinaryTree[T]:
24
+ """
25
+ Balance the tree using AVL rotations.
26
+
27
+ :return: The balanced tree.
28
+ """
29
+ self.tree = self._balance(self.tree)
30
+ return self.tree
31
+
32
+ def insert(self, value: T) -> BinaryNode[T]:
33
+ """
34
+ Insert a value into the tree and balance it.
35
+
36
+ :param value: The value to insert.
37
+ :return: The inserted node.
38
+ """
39
+ node = self.tree.insert(value)
40
+ self.balance()
41
+ return node
42
+
43
+ def delete(self, value: T) -> bool:
44
+ """
45
+ Delete a value from the tree and balance it.
46
+
47
+ :param value: The value to delete.
48
+ :return: True if the value was deleted, otherwise False.
49
+ """
50
+ deleted = self.tree.delete(value)
51
+ self.balance()
52
+ return deleted
53
+
54
+ def find(self, value: T) -> BinaryNode[T] | None:
55
+ """
56
+ Find a value in the tree.
57
+
58
+ :param value: The value to find.
59
+ :return: The node containing the value, or None if not found.
60
+ """
61
+ return self.tree.find(value)
62
+
63
+ def contains(self, value: T) -> bool:
64
+ """
65
+ Check whether the tree contains a value.
66
+
67
+ :param value: The value to check.
68
+ :return: True if the value exists, otherwise False.
69
+ """
70
+ return self.tree.contains(value)
71
+
72
+ def __contains__(self, value: T) -> bool:
73
+ return value in self.tree
74
+
75
+ def height(self) -> int:
76
+ """
77
+ Return the height of the tree.
78
+
79
+ :return: The tree height.
80
+ """
81
+ return self.tree.height()
82
+
83
+ def count_leaves(self) -> int:
84
+ """
85
+ Count the leaf nodes in the tree.
86
+
87
+ :return: The number of leaf nodes.
88
+ """
89
+ return self.tree.count_leaves()
90
+
91
+ def count_internal_nodes(self) -> int:
92
+ """
93
+ Count the internal nodes in the tree.
94
+
95
+ :return: The number of internal nodes.
96
+ """
97
+ return self.tree.count_internal_nodes()
98
+
99
+ def get_root(self) -> BinaryNode[T] | None:
100
+ """
101
+ Return the root node.
102
+
103
+ :return: The root node, or None if the tree is empty.
104
+ """
105
+ return self.tree.get_root()
106
+
107
+ def get_left_subtree(self) -> BinaryTree[T]:
108
+ """
109
+ Return the left subtree.
110
+
111
+ :return: The left subtree.
112
+ """
113
+ return self.tree.get_left_subtree()
114
+
115
+ def get_right_subtree(self) -> BinaryTree[T]:
116
+ """
117
+ Return the right subtree.
118
+
119
+ :return: The right subtree.
120
+ """
121
+ return self.tree.get_right_subtree()
122
+
123
+ def _set_left_subtree(self, subtree: BinaryTree[T]) -> None:
124
+ """
125
+ Set the left subtree.
126
+
127
+ :param subtree: The tree to use as the left subtree.
128
+ """
129
+ self.tree._set_left_subtree(subtree)
130
+
131
+ def _set_right_subtree(self, subtree: BinaryTree[T]) -> None:
132
+ """
133
+ Set the right subtree.
134
+
135
+ :param subtree: The tree to use as the right subtree.
136
+ """
137
+ self.tree._set_right_subtree(subtree)
138
+
139
+ def right_rotate(self) -> BinaryTree[T]:
140
+ """
141
+ Perform a right rotation.
142
+
143
+ :return: The tree after the rotation.
144
+ """
145
+ self.tree = self.tree.right_rotate()
146
+ return self.tree
147
+
148
+ def left_rotate(self) -> BinaryTree[T]:
149
+ """
150
+ Perform a left rotation.
151
+
152
+ :return: The tree after the rotation.
153
+ """
154
+ self.tree = self.tree.left_rotate()
155
+ return self.tree
156
+
157
+ def invert_tree(self) -> None:
158
+ """
159
+ Invert the tree.
160
+ """
161
+ self.tree.invert_tree()
162
+
163
+ def is_empty(self) -> bool:
164
+ """
165
+ Check whether the tree is empty.
166
+
167
+ :return: True if the tree is empty, otherwise False.
168
+ """
169
+ return self.tree.is_empty()
170
+
171
+ def clear(self) -> None:
172
+ """
173
+ Clear the tree.
174
+ """
175
+ self.tree.clear()
176
+
177
+ def size(self) -> int:
178
+ """
179
+ Return the number of nodes in the tree.
180
+
181
+ :return: The tree size.
182
+ """
183
+ return self.tree.size()
184
+
185
+ def __len__(self) -> int:
186
+ return len(self.tree)
187
+
188
+ def __bool__(self) -> bool:
189
+ return bool(self.tree)
190
+
191
+ def _balance(self, tree: BinaryTree[T]) -> BinaryTree[T]:
192
+ """
193
+ Recursively balance the given tree.
194
+
195
+ :param tree: The tree to balance.
196
+ :return: The balanced tree.
197
+ """
198
+ if tree.is_empty():
199
+ return tree
200
+
201
+ left_subtree = self._balance(tree.get_left_subtree())
202
+ right_subtree = self._balance(tree.get_right_subtree())
203
+
204
+ tree._set_left_subtree(left_subtree)
205
+ tree._set_right_subtree(right_subtree)
206
+
207
+ return self._rebalance(tree)
208
+
209
+ def _rebalance(self, tree: BinaryTree[T]) -> BinaryTree[T]:
210
+ """
211
+ Rebalance the given tree if it violates the AVL balance condition.
212
+
213
+ :param tree: The tree to rebalance.
214
+ :return: The rebalanced tree.
215
+ """
216
+ balance_factor = self._calculate_balance_factor(tree)
217
+
218
+ if balance_factor > self.balance_factor:
219
+ left_subtree = tree.get_left_subtree()
220
+
221
+ if self._calculate_balance_factor(left_subtree) < 0:
222
+ left_subtree.left_rotate()
223
+ tree._set_left_subtree(left_subtree)
224
+
225
+ return tree.right_rotate()
226
+
227
+ if balance_factor < -self.balance_factor:
228
+ right_subtree = tree.get_right_subtree()
229
+
230
+ if self._calculate_balance_factor(right_subtree) > 0:
231
+ right_subtree.right_rotate()
232
+ tree._set_right_subtree(right_subtree)
233
+
234
+ return tree.left_rotate()
235
+
236
+ return tree
237
+
238
+ def _calculate_balance_factor(self, tree: BinaryTree[T]) -> int:
239
+ """
240
+ Return the AVL balance factor of the tree.
241
+
242
+ :param tree: The tree whose balance factor is calculated.
243
+ :return: The height difference between the left and right subtrees.
244
+ """
245
+ return tree.get_left_subtree().height() - tree.get_right_subtree().height()
@@ -1,10 +1,11 @@
1
1
  from abc import abstractmethod
2
+ from typing import Protocol
2
3
 
3
4
  from stepps.nodes import BinaryNode
4
5
  from stepps.trees.tree import Tree
5
6
 
6
7
 
7
- class BinaryTree[T](Tree[T]):
8
+ class BinaryTree[T](Tree[T], Protocol):
8
9
  """
9
10
  Define the interface for binary tree implementations.
10
11
  """
@@ -116,19 +117,39 @@ class BinaryTree[T](Tree[T]):
116
117
  ...
117
118
 
118
119
  @abstractmethod
119
- def right_rotate(self) -> "BinaryTree[T] | None":
120
+ def right_rotate(self) -> "BinaryTree[T]":
120
121
  """
121
122
  Perform a right rotation on the tree.
122
123
 
123
- :return: The new root node after the rotation.
124
+ :return: The tree after the rotation.
124
125
  """
125
126
  ...
126
127
 
127
128
  @abstractmethod
128
- def left_rotate(self) -> "BinaryTree[T] | None":
129
+ def left_rotate(self) -> "BinaryTree[T]":
129
130
  """
130
131
  Perform a left rotation on the tree.
131
132
 
132
- :return: The new root node after the rotation.
133
+ :return: The tree after the rotation.
134
+ """
135
+ ...
136
+
137
+ @abstractmethod
138
+ def _set_left_subtree(self, subtree: "BinaryTree[T]") -> None:
139
+ """
140
+ Set the left subtree.
141
+
142
+ :param subtree: The tree to use as the left subtree.
143
+ :return: ``None``.
144
+ """
145
+ ...
146
+
147
+ @abstractmethod
148
+ def _set_right_subtree(self, subtree: "BinaryTree[T]") -> None:
149
+ """
150
+ Set the right subtree.
151
+
152
+ :param subtree: The tree to use as the right subtree.
153
+ :return: ``None``.
133
154
  """
134
155
  ...
@@ -305,3 +305,27 @@ class BinaryTreeImpl[T](BinaryTree[T]):
305
305
  self.root = new_root
306
306
 
307
307
  return self
308
+
309
+ @override
310
+ def _set_left_subtree(self, subtree: BinaryTree[T]) -> None:
311
+ """
312
+ Set the left subtree.
313
+
314
+ :param subtree: The tree to use as the left subtree.
315
+ """
316
+ if self.root is None:
317
+ return
318
+
319
+ self.root.left = subtree.get_root()
320
+
321
+ @override
322
+ def _set_right_subtree(self, subtree: BinaryTree[T]) -> None:
323
+ """
324
+ Set the right subtree.
325
+
326
+ :param subtree: The tree to use as the right subtree.
327
+ """
328
+ if self.root is None:
329
+ return
330
+
331
+ self.root.right = subtree.get_root()
@@ -15,7 +15,7 @@ class Comparable(Protocol):
15
15
  def __gt__(self, other: object, /) -> bool: ...
16
16
 
17
17
 
18
- class BST[T: Comparable](BinaryTree[T]):
18
+ class BST[T: Comparable](BinaryTree[T], Protocol):
19
19
  """
20
20
  Define the interface for binary search tree implementations.
21
21
 
@@ -1,9 +1,10 @@
1
- from abc import ABC, abstractmethod
1
+ from abc import abstractmethod
2
+ from typing import Protocol
2
3
 
3
4
  from stepps.nodes import BinaryNode
4
5
 
5
6
 
6
- class Tree[T](ABC):
7
+ class Tree[T](Protocol):
7
8
  """
8
9
  Define the common interface for tree implementations.
9
10
  """
@@ -1,10 +1,11 @@
1
1
  from abc import abstractmethod
2
+ from typing import Protocol
2
3
 
3
4
  from stepps.nodes import BinaryNode
4
5
  from stepps.visualizer.tree_visualizer import TreeVisualizer
5
6
 
6
7
 
7
- class CliTreeVisualizer[T](TreeVisualizer[BinaryNode[T] | None]):
8
+ class CliTreeVisualizer[T](TreeVisualizer[BinaryNode[T] | None], Protocol):
8
9
  """
9
10
  Define the interface for CLI-based binary tree visualization.
10
11
  """
@@ -1,7 +1,8 @@
1
- from abc import ABC, abstractmethod
1
+ from abc import abstractmethod
2
+ from typing import Protocol
2
3
 
3
4
 
4
- class TreeVisualizer[T](ABC):
5
+ class TreeVisualizer[T](Protocol):
5
6
  """
6
7
  Define the interface for tree visualization.
7
8
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stepps
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Useful algorithms in Python
5
5
  Author-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>, Trinay Mitra <trinay.mitra@vaastav.tech>
6
6
  Maintainer-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>
@@ -7,16 +7,6 @@ src/stepps.egg-info/PKG-INFO
7
7
  src/stepps.egg-info/SOURCES.txt
8
8
  src/stepps.egg-info/dependency_links.txt
9
9
  src/stepps.egg-info/top_level.txt
10
- src/stepps/TreeBuilder/__init__.py
11
- src/stepps/TreeBuilder/inorder_tree_builder.py
12
- src/stepps/TreeBuilder/inorder_tree_builder_impl.py
13
- src/stepps/TreeBuilder/level_order_tree_builder.py
14
- src/stepps/TreeBuilder/level_order_tree_builder_impl.py
15
- src/stepps/TreeBuilder/postorder_tree_builder.py
16
- src/stepps/TreeBuilder/postorder_tree_builder_impl.py
17
- src/stepps/TreeBuilder/preorder_tree_builder.py
18
- src/stepps/TreeBuilder/preorder_tree_builder_impl.py
19
- src/stepps/TreeBuilder/tree_builder.py
20
10
  src/stepps/iterators/__init__.py
21
11
  src/stepps/iterators/base_iterator.py
22
12
  src/stepps/iterators/inorder.py
@@ -25,16 +15,30 @@ src/stepps/iterators/postorder.py
25
15
  src/stepps/iterators/preorder.py
26
16
  src/stepps/nodes/__init__.py
27
17
  src/stepps/nodes/binary_node.py
18
+ src/stepps/tree_builder/__init__.py
19
+ src/stepps/tree_builder/inorder_tree_builder.py
20
+ src/stepps/tree_builder/inorder_tree_builder_impl.py
21
+ src/stepps/tree_builder/level_order_tree_builder.py
22
+ src/stepps/tree_builder/level_order_tree_builder_impl.py
23
+ src/stepps/tree_builder/postorder_tree_builder.py
24
+ src/stepps/tree_builder/postorder_tree_builder_impl.py
25
+ src/stepps/tree_builder/preorder_tree_builder.py
26
+ src/stepps/tree_builder/preorder_tree_builder_impl.py
27
+ src/stepps/tree_builder/tree_builder.py
28
28
  src/stepps/trees/__init__.py
29
29
  src/stepps/trees/binary_tree.py
30
30
  src/stepps/trees/binary_tree_impl.py
31
31
  src/stepps/trees/bst.py
32
32
  src/stepps/trees/bst_impl.py
33
33
  src/stepps/trees/tree.py
34
+ src/stepps/trees/balanced/__init__.py
35
+ src/stepps/trees/balanced/avl.py
36
+ src/stepps/trees/balanced/avl_impl.py
34
37
  src/stepps/visualizer/__init__.py
35
38
  src/stepps/visualizer/cli_visualizer.py
36
39
  src/stepps/visualizer/cli_visualizer_impl.py
37
40
  src/stepps/visualizer/tree_visualizer.py
41
+ tests/test_avl.py
38
42
  tests/test_binary_tree.py
39
43
  tests/test_bst.py
40
44
  tests/test_inorder_tree_builder.py
@@ -0,0 +1,368 @@
1
+ from stepps.iterators.levelorder import LevelOrderIterator
2
+ from stepps.nodes import BinaryNode
3
+ from stepps.trees.balanced.avl_impl import AVLImpl
4
+ from stepps.trees.binary_tree_impl import BinaryTreeImpl
5
+
6
+
7
+ def create_tree(root: BinaryNode[int], size: int) -> BinaryTreeImpl[int]:
8
+ tree = BinaryTreeImpl[int](LevelOrderIterator)
9
+ tree.root = root
10
+ tree._size = size
11
+
12
+ return tree
13
+
14
+
15
+ # =====================================================
16
+ # Basic Cases
17
+ # =====================================================
18
+
19
+
20
+ def test_balance_empty_tree():
21
+ tree = BinaryTreeImpl[int](LevelOrderIterator)
22
+ avl = AVLImpl[int](tree)
23
+
24
+ result = avl.balance()
25
+
26
+ assert result is tree
27
+ assert result.is_empty()
28
+ assert result.size() == 0
29
+
30
+
31
+ def test_balance_single_node_tree():
32
+ tree = create_tree(BinaryNode(10), 1)
33
+ avl = AVLImpl[int](tree)
34
+
35
+ result = avl.balance()
36
+
37
+ assert result is tree
38
+ assert result.root is not None
39
+ assert result.root.value == 10
40
+ assert result.root.left is None
41
+ assert result.root.right is None
42
+ assert result.size() == 1
43
+
44
+
45
+ def test_balance_already_balanced_tree():
46
+ root = BinaryNode(20)
47
+ root.left = BinaryNode(10)
48
+ root.right = BinaryNode(30)
49
+
50
+ tree = create_tree(root, 3)
51
+ avl = AVLImpl[int](tree)
52
+
53
+ result = avl.balance()
54
+
55
+ assert result is tree
56
+ assert result.root is not None
57
+ assert result.root.value == 20
58
+
59
+ assert result.root.left is not None
60
+ assert result.root.left.value == 10
61
+
62
+ assert result.root.right is not None
63
+ assert result.root.right.value == 30
64
+
65
+
66
+ # =====================================================
67
+ # Basic AVL Rotation Cases
68
+ # =====================================================
69
+
70
+
71
+ def test_balance_ll_case():
72
+ root = BinaryNode(30)
73
+ root.left = BinaryNode(20)
74
+ root.left.left = BinaryNode(10)
75
+
76
+ tree = create_tree(root, 3)
77
+ avl = AVLImpl[int](tree)
78
+
79
+ result = avl.balance()
80
+
81
+ assert result.root is not None
82
+ assert result.root.value == 20
83
+
84
+ assert result.root.left is not None
85
+ assert result.root.left.value == 10
86
+
87
+ assert result.root.right is not None
88
+ assert result.root.right.value == 30
89
+
90
+
91
+ def test_balance_rr_case():
92
+ root = BinaryNode(10)
93
+ root.right = BinaryNode(20)
94
+ root.right.right = BinaryNode(30)
95
+
96
+ tree = create_tree(root, 3)
97
+ avl = AVLImpl[int](tree)
98
+
99
+ result = avl.balance()
100
+
101
+ assert result.root is not None
102
+ assert result.root.value == 20
103
+
104
+ assert result.root.left is not None
105
+ assert result.root.left.value == 10
106
+
107
+ assert result.root.right is not None
108
+ assert result.root.right.value == 30
109
+
110
+
111
+ def test_balance_lr_case():
112
+ root = BinaryNode(30)
113
+ root.left = BinaryNode(10)
114
+ root.left.right = BinaryNode(20)
115
+
116
+ tree = create_tree(root, 3)
117
+ avl = AVLImpl[int](tree)
118
+
119
+ result = avl.balance()
120
+
121
+ assert result.root is not None
122
+ assert result.root.value == 20
123
+
124
+ assert result.root.left is not None
125
+ assert result.root.left.value == 10
126
+
127
+ assert result.root.right is not None
128
+ assert result.root.right.value == 30
129
+
130
+
131
+ def test_balance_rl_case():
132
+ root = BinaryNode(10)
133
+ root.right = BinaryNode(30)
134
+ root.right.left = BinaryNode(20)
135
+
136
+ tree = create_tree(root, 3)
137
+ avl = AVLImpl[int](tree)
138
+
139
+ result = avl.balance()
140
+
141
+ assert result.root is not None
142
+ assert result.root.value == 20
143
+
144
+ assert result.root.left is not None
145
+ assert result.root.left.value == 10
146
+
147
+ assert result.root.right is not None
148
+ assert result.root.right.value == 30
149
+
150
+
151
+ # =====================================================
152
+ # Nested Subtree Cases
153
+ # =====================================================
154
+
155
+
156
+ def test_balance_ll_case_in_left_subtree():
157
+ root = BinaryNode(100)
158
+ root.left = BinaryNode(50)
159
+ root.left.left = BinaryNode(30)
160
+ root.left.left.left = BinaryNode(20)
161
+
162
+ tree = create_tree(root, 4)
163
+ avl = AVLImpl[int](tree)
164
+
165
+ result = avl.balance()
166
+
167
+ assert result.root is not None
168
+ assert result.root.value == 30
169
+
170
+ assert result.root.left is not None
171
+ assert result.root.left.value == 20
172
+
173
+ assert result.root.right is not None
174
+ assert result.root.right.value == 100
175
+
176
+ assert result.root.right.left is not None
177
+ assert result.root.right.left.value == 50
178
+
179
+
180
+ def test_balance_rr_case_in_right_subtree():
181
+ root = BinaryNode(100)
182
+ root.right = BinaryNode(150)
183
+ root.right.right = BinaryNode(170)
184
+ root.right.right.right = BinaryNode(180)
185
+
186
+ tree = create_tree(root, 4)
187
+ avl = AVLImpl[int](tree)
188
+
189
+ result = avl.balance()
190
+
191
+ assert result.root is not None
192
+ assert result.root.value == 170
193
+
194
+ assert result.root.left is not None
195
+ assert result.root.left.value == 100
196
+
197
+ assert result.root.left.right is not None
198
+ assert result.root.left.right.value == 150
199
+
200
+ assert result.root.right is not None
201
+ assert result.root.right.value == 180
202
+
203
+
204
+ def test_balance_lr_case_in_left_subtree():
205
+ root = BinaryNode(100)
206
+ root.left = BinaryNode(50)
207
+ root.left.left = BinaryNode(30)
208
+ root.left.left.right = BinaryNode(40)
209
+
210
+ tree = create_tree(root, 4)
211
+ avl = AVLImpl[int](tree)
212
+
213
+ result = avl.balance()
214
+
215
+ assert result.root is not None
216
+ assert result.root.value == 40
217
+
218
+ assert result.root.left is not None
219
+ assert result.root.left.value == 30
220
+
221
+ assert result.root.right is not None
222
+ assert result.root.right.value == 100
223
+
224
+ assert result.root.right.left is not None
225
+ assert result.root.right.left.value == 50
226
+
227
+
228
+ def test_balance_rl_case_in_right_subtree():
229
+ root = BinaryNode(100)
230
+ root.right = BinaryNode(150)
231
+ root.right.right = BinaryNode(170)
232
+ root.right.right.left = BinaryNode(160)
233
+
234
+ tree = create_tree(root, 4)
235
+ avl = AVLImpl[int](tree)
236
+
237
+ result = avl.balance()
238
+
239
+ assert result.root is not None
240
+ assert result.root.value == 160
241
+
242
+ assert result.root.left is not None
243
+ assert result.root.left.value == 100
244
+
245
+ assert result.root.left.right is not None
246
+ assert result.root.left.right.value == 150
247
+
248
+ assert result.root.right is not None
249
+ assert result.root.right.value == 170
250
+
251
+
252
+ # =====================================================
253
+ # Larger Trees
254
+ # =====================================================
255
+
256
+
257
+ def test_balance_left_heavy_tree():
258
+ root = BinaryNode(50)
259
+
260
+ root.left = BinaryNode(30)
261
+ root.left.left = BinaryNode(20)
262
+ root.left.left.left = BinaryNode(10)
263
+ root.left.right = BinaryNode(40)
264
+
265
+ root.right = BinaryNode(70)
266
+ root.right.left = BinaryNode(60)
267
+ root.right.right = BinaryNode(80)
268
+
269
+ tree = create_tree(root, 8)
270
+ avl = AVLImpl[int](tree)
271
+
272
+ result = avl.balance()
273
+
274
+ assert result.root is not None
275
+ assert result.height() <= 3
276
+ assert result.size() == 8
277
+
278
+ for value in (10, 20, 30, 40, 50, 60, 70, 80):
279
+ assert result.contains(value)
280
+
281
+
282
+ def test_balance_right_heavy_tree():
283
+ root = BinaryNode(50)
284
+
285
+ root.left = BinaryNode(30)
286
+ root.left.left = BinaryNode(20)
287
+ root.left.right = BinaryNode(40)
288
+
289
+ root.right = BinaryNode(70)
290
+ root.right.right = BinaryNode(80)
291
+ root.right.right.right = BinaryNode(90)
292
+
293
+ tree = create_tree(root, 7)
294
+ avl = AVLImpl[int](tree)
295
+
296
+ result = avl.balance()
297
+
298
+ assert result.root is not None
299
+ assert result.height() <= 3
300
+ assert result.size() == 7
301
+
302
+ for value in (20, 30, 40, 50, 70, 80, 90):
303
+ assert result.contains(value)
304
+
305
+
306
+ def test_balance_complex_tree():
307
+ root = BinaryNode(50)
308
+
309
+ root.left = BinaryNode(30)
310
+ root.left.left = BinaryNode(20)
311
+ root.left.right = BinaryNode(40)
312
+ root.left.left.left = BinaryNode(10)
313
+
314
+ root.right = BinaryNode(70)
315
+ root.right.right = BinaryNode(80)
316
+ root.right.right.right = BinaryNode(90)
317
+
318
+ tree = create_tree(root, 8)
319
+ avl = AVLImpl[int](tree)
320
+
321
+ result = avl.balance()
322
+
323
+ assert result.root is not None
324
+ assert result.height() <= 3
325
+ assert result.size() == 8
326
+
327
+ for value in (10, 20, 30, 40, 50, 70, 80, 90):
328
+ assert result.contains(value)
329
+
330
+
331
+ # =====================================================
332
+ # Tree Structure Preservation
333
+ # =====================================================
334
+
335
+
336
+ def test_balance_preserves_all_nodes():
337
+ root = BinaryNode(50)
338
+
339
+ root.left = BinaryNode(30)
340
+ root.left.left = BinaryNode(20)
341
+ root.left.right = BinaryNode(40)
342
+
343
+ root.right = BinaryNode(70)
344
+ root.right.left = BinaryNode(60)
345
+ root.right.right = BinaryNode(80)
346
+
347
+ tree = create_tree(root, 7)
348
+ avl = AVLImpl[int](tree)
349
+
350
+ result = avl.balance()
351
+
352
+ assert result.size() == 7
353
+
354
+ for value in (20, 30, 40, 50, 60, 70, 80):
355
+ assert result.contains(value)
356
+
357
+
358
+ def test_balance_returns_same_tree_object():
359
+ root = BinaryNode(30)
360
+ root.left = BinaryNode(20)
361
+ root.left.left = BinaryNode(10)
362
+
363
+ tree = create_tree(root, 3)
364
+ avl = AVLImpl[int](tree)
365
+
366
+ result = avl.balance()
367
+
368
+ assert result is tree
@@ -390,3 +390,78 @@ def test_left_rotate_without_right_child():
390
390
  assert result is tree
391
391
  assert tree.root is not None
392
392
  assert tree.root.value == 30
393
+
394
+
395
+ # =====================================================
396
+ # Tree Subtree Assignment
397
+ # =====================================================
398
+
399
+
400
+ def test_set_left_subtree(tree):
401
+ subtree = BinaryTreeImpl[int](LevelOrderIterator)
402
+
403
+ subtree.root = BinaryNode(30)
404
+ subtree.root.left = BinaryNode(20)
405
+ subtree.root.right = BinaryNode(40)
406
+ subtree._size = 3
407
+
408
+ tree._set_left_subtree(subtree)
409
+
410
+ assert tree.root is not None
411
+ assert tree.root.left is subtree.root
412
+ assert tree.root.left.value == 30
413
+
414
+ assert tree.root.left.left is not None
415
+ assert tree.root.left.left.value == 20
416
+
417
+ assert tree.root.left.right is not None
418
+ assert tree.root.left.right.value == 40
419
+
420
+
421
+ def test_set_right_subtree(tree):
422
+ subtree = BinaryTreeImpl[int](LevelOrderIterator)
423
+
424
+ subtree.root = BinaryNode(70)
425
+ subtree.root.left = BinaryNode(60)
426
+ subtree.root.right = BinaryNode(80)
427
+ subtree._size = 3
428
+
429
+ tree._set_right_subtree(subtree)
430
+
431
+ assert tree.root is not None
432
+ assert tree.root.right is subtree.root
433
+ assert tree.root.right.value == 70
434
+
435
+ assert tree.root.right.left is not None
436
+ assert tree.root.right.left.value == 60
437
+
438
+ assert tree.root.right.right is not None
439
+ assert tree.root.right.right.value == 80
440
+
441
+
442
+ def test_right_rotate_nested_subtree():
443
+ tree = BinaryTreeImpl[int](LevelOrderIterator)
444
+
445
+ root = BinaryNode(100)
446
+ root.left = BinaryNode(50)
447
+ root.left.left = BinaryNode(30)
448
+ root.left.left.left = BinaryNode(20)
449
+
450
+ tree.root = root
451
+ tree._size = 4
452
+
453
+ subtree = tree.get_left_subtree()
454
+ subtree.right_rotate()
455
+ tree._set_left_subtree(subtree)
456
+
457
+ assert tree.root is not None
458
+ assert tree.root.value == 100
459
+
460
+ assert tree.root.left is not None
461
+ assert tree.root.left.value == 30
462
+
463
+ assert tree.root.left.left is not None
464
+ assert tree.root.left.left.value == 20
465
+
466
+ assert tree.root.left.right is not None
467
+ assert tree.root.left.right.value == 50
@@ -1,5 +1,5 @@
1
1
  from stepps.iterators.inorder import InOrderIterator
2
- from stepps.TreeBuilder.inorder_tree_builder_impl import InOrderTreeBuilderImpl
2
+ from stepps.tree_builder.inorder_tree_builder_impl import InOrderTreeBuilderImpl
3
3
 
4
4
 
5
5
  def test_build_empty_sequence():
@@ -1,5 +1,5 @@
1
1
  from stepps.iterators.levelorder import LevelOrderIterator
2
- from stepps.TreeBuilder.level_order_tree_builder_impl import LevelOrderTreeBuilderImpl
2
+ from stepps.tree_builder.level_order_tree_builder_impl import LevelOrderTreeBuilderImpl
3
3
 
4
4
 
5
5
  def test_build_empty_sequence():
@@ -1,5 +1,5 @@
1
1
  from stepps.iterators.postorder import PostOrderIterator
2
- from stepps.TreeBuilder.postorder_tree_builder_impl import PostOrderTreeBuilderImpl
2
+ from stepps.tree_builder.postorder_tree_builder_impl import PostOrderTreeBuilderImpl
3
3
 
4
4
 
5
5
  def test_build_empty_sequence():
@@ -1,5 +1,5 @@
1
1
  from stepps.iterators.preorder import PreOrderIterator
2
- from stepps.TreeBuilder.preorder_tree_builder_impl import PreOrderTreeBuilderImpl
2
+ from stepps.tree_builder.preorder_tree_builder_impl import PreOrderTreeBuilderImpl
3
3
 
4
4
 
5
5
  def test_build_empty_sequence():
@@ -1,13 +0,0 @@
1
- from stepps.TreeBuilder.inorder_tree_builder import InOrderTreeBuilder
2
- from stepps.TreeBuilder.inorder_tree_builder_impl import InOrderTreeBuilderImpl
3
- from stepps.TreeBuilder.level_order_tree_builder import LevelOrderTreeBuilder
4
- from stepps.TreeBuilder.level_order_tree_builder_impl import LevelOrderTreeBuilderImpl
5
- from stepps.TreeBuilder.tree_builder import TreeBuilder
6
-
7
- __all__ = [
8
- "InOrderTreeBuilder",
9
- "InOrderTreeBuilderImpl",
10
- "LevelOrderTreeBuilder",
11
- "LevelOrderTreeBuilderImpl",
12
- "TreeBuilder",
13
- ]
@@ -1,8 +0,0 @@
1
- from stepps.TreeBuilder.tree_builder import TreeBuilder
2
-
3
-
4
- class InOrderTreeBuilder[T](TreeBuilder[T]):
5
- """
6
- Define the interface for building a binary tree from a sequence
7
- using inorder traversal.
8
- """
@@ -1,8 +0,0 @@
1
- from stepps.TreeBuilder.tree_builder import TreeBuilder
2
-
3
-
4
- class LevelOrderTreeBuilder[T](TreeBuilder[T]):
5
- """
6
- Define the interface for building a binary tree from a sequence
7
- using level-order traversal.
8
- """
@@ -1,8 +0,0 @@
1
- from stepps.TreeBuilder.tree_builder import TreeBuilder
2
-
3
-
4
- class PostOrderTreeBuilder[T](TreeBuilder[T]):
5
- """
6
- Define the interface for building a binary tree from a sequence
7
- using postorder traversal.
8
- """
@@ -1,8 +0,0 @@
1
- from stepps.TreeBuilder.tree_builder import TreeBuilder
2
-
3
-
4
- class PreOrderTreeBuilder[T](TreeBuilder[T]):
5
- """
6
- Define the interface for building a binary tree from a sequence
7
- using preorder traversal.
8
- """
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes