dsa.tree

Module containing tree class.

  1""" Module containing tree class. """
  2
  3class TreeNode:
  4    """ 
  5    A binary tree node implementation.
  6    """
  7    def __init__(self, value, left=None, right=None):
  8        """ 
  9        TreeNode constructor.
 10        
 11        Args:
 12            value: The value of the node.
 13            left: Reference to the left node.
 14            right: Reference to the right node.
 15        """
 16        self.value = value
 17        self.left = left
 18        self.right = right
 19
 20    def copy(self):
 21        """
 22        Return a copy of the node.
 23        """
 24        new_node = TreeNode(self.value) 
 25        if self.left:
 26            new_node.left = self.left.copy()
 27        if self.right:
 28            new_node.right = self.right.copy()
 29        return new_node
 30    
 31    def print(self, level=0):
 32        """ 
 33        Print the contents of a node.
 34
 35        Args:
 36            level: starting level of node
 37        """
 38        if self.right:
 39            self.right.print(level + 1)
 40        print("   " * level + str(self.value))
 41        if self.left:
 42            self.left.print(level + 1)
 43
 44    def __lt__(self, other):
 45        """
 46        Compare the value of two nodes.
 47        """
 48        return self.value < other.value
 49    
 50    def __repr__(self):
 51        """
 52        Return the string representation of a node.
 53        """
 54        if self.value is None:
 55            return "none"
 56        else:
 57            return str(self.value)
 58
 59class Tree:
 60    """ 
 61    A binary search tree (BST) implementation. Can be treated as a plain binary tree if operations (insert, search, delete) are not used and nodes are set manually.
 62    """
 63    def __init__(self, root=None):
 64        """ 
 65        Args:
 66            root: Root node of the Tree.
 67        """
 68        self.root = root
 69        
 70    def search(self, value):
 71        """ 
 72        Search for a value in the binary search tree.
 73
 74        Args:
 75            value: Value to search for.
 76        
 77        Returns:
 78            node with matching value
 79
 80        Raises:
 81            ValueError: if value is not found
 82        """
 83        current = self.root
 84        
 85        while current is not None:
 86            if value == current.value:
 87                return current
 88            elif value < current.value:
 89                current = current.left
 90            elif value > current.value:
 91                current = current.right
 92
 93        raise ValueError(f"Value {value} not found in the tree")
 94
 95    def insert(self, value):
 96        """ 
 97        Insert a value in the binary search tree.
 98
 99        Args:
100            value: The value to insert.
101        
102        Returns:
103            The root of the tree after deletion.
104
105        Raises:
106            ValueError: if value is already in the tree
107        """
108        self.root = self.insert_rec(self.root, value)
109        return self.root
110    
111    def insert_rec(self, root, value):
112        if root is None:
113            return TreeNode(value)
114        if value < root.value:
115            root.left = self.insert_rec(root.left, value)
116        elif value > root.value:
117            root.right = self.insert_rec(root.right, value)
118        else:
119            raise ValueError("Value already exists in the tree")
120        return root
121
122    def insert_iterative(self, value):
123        """ 
124        Insert a value in the binary search tree (iterative implementation).
125
126        Args:
127            value: The value to insert.
128        
129        Returns:
130            None
131
132        Raises:
133            ValueError: if value is already in the tree
134        """
135        current = self.root
136        if self.root is None:
137            self.root = TreeNode(value)
138            return
139        
140        while current is not None:
141            if value < current.value:
142                if current.left is None:
143                    current.left = TreeNode(value)
144                    return
145                else:
146                    current = current.left
147            elif value > current.value:
148                if current.right is None:
149                    current.right = TreeNode(value)
150                    return
151                else:
152                    current = current.right
153            else:
154                return 
155   
156    def delete(self, value):
157        """ 
158        Delete a value from the binary search tree.
159
160        Args:
161            value: The value to delete.
162        
163        Returns:
164            The root of the tree after insertion.
165
166        
167        Raises:
168            ValueError: if value is not found.
169        """
170        self.root = self.delete_node(self.root, value)
171        return self.root
172        
173    def delete_node(self, node, value):
174        """ 
175        Helper function to delete a value from the binary search tree. (Use delete() instead)
176
177        Args:
178            value: The value to delete.
179            node: The current node.
180
181        Returns:
182            The new subtree root after deletion.
183        
184        Raises:
185            ValueError: if value is not found.
186        """ 
187        if node is None:
188            raise ValueError("Value not found in the tree")
189            return None
190        
191        if value < node.value:
192            node.left = self.delete_node(node.left, value)
193        elif value > node.value:
194            node.right = self.delete_node(node.right, value)
195        else:
196            if node.left is None:
197                subtree = node.right
198                node = None
199                return subtree
200            elif node.right is None:
201                subtree = node.left
202                node = None
203                return subtree
204            
205            successor = self.successor_node(node.right)
206            node.value = successor.value
207            node.right = self.delete_node(node.right, successor.value)
208            
209        return node
210    
211    def successor_node(self, node=None):
212        """ 
213        Return the successor node (the minimum value in a binary search tree's right subtree).
214
215        Args:
216            node: The starting node.
217        
218        Returns:
219            node with the lowest value in the BST
220            None if not found
221
222        Raises:
223            ValueError: if value is not found.
224        """
225        if node is None:
226            node = self.root
227        
228        if node.left is None:
229            return node
230        else:
231            return self.successor_node(node.left)
232    
233    def predecessor_node(self, node=None):
234        """ 
235        Return the predecessor node (the maximum value in a binary search tree's left subtree).
236
237        Args:
238            node: The starting node.
239        
240        Returns:
241            node with the lowest value in the BST
242            None if not found
243
244        Raises:
245            ValueError: if value is not found.
246        """
247        if node is None:
248            node = self.root
249        
250        if node.right is None:
251            return node
252        else:
253            return self.predecessor_node(node.right)
254    
255    def delete_iterative(self, root, value):
256        """ 
257        Delete a value from the binary search tree (iterative version).
258
259        Args:
260            value: The value to delete.
261        
262        Returns:
263            The root of the tree after insertion.
264
265        
266        Raises:
267            ValueError: if value is not found.
268        """
269        parent = None
270        current = root
271
272        # Find the node to delete and its parent
273        while current and current.key != value:
274            parent = current
275            if value < current.key:
276                current = current.left
277            else:
278                current = current.right
279
280        if current is None:
281            return root  # Key not found
282
283        # Case 1 & 2: Node has at most one child
284        def replace_node_in_parent(new_child):
285            if parent is None:
286                return new_child  # Deleting the root node
287            if parent.left == current:
288                parent.left = new_child
289            else:
290                parent.right = new_child
291            return root
292
293        if current.left is None:
294            return replace_node_in_parent(current.right)
295        elif current.right is None:
296            return replace_node_in_parent(current.left)
297
298        # Case 3: Node has two children
299        # Find in-order successor and its parent
300        successor_parent = current
301        successor = current.right
302        while successor.left:
303            successor_parent = successor
304            successor = successor.left
305
306        # Replace current's key with successor's key
307        current.key = successor.key
308
309        # Delete successor node (which has at most one child)
310        if successor_parent.left == successor:
311            successor_parent.left = successor.right
312        else:
313            successor_parent.right = successor.right
314
315        return root
316
317    def print(self):
318        """ 
319        Print the values in the BST.
320        """
321        self.root.print()
322        
323    def __len__(self):
324        """ 
325        Return the number of nodes in the BST.
326        
327        Returns:
328            The number of nodes in the BST.
329        """
330        def count_nodes(node):
331            if node is None:
332                return 0
333            return 1 + count_nodes(node.left) + count_nodes(node.right)
334        
335        return count_nodes(self.root)
336        
337
338    def __eq__(self, other):
339        """
340        Compare two Tree objects for value-based equality (structure and values).
341
342        Returns:
343            True if both are Tree instances and their structures and values are equal, False otherwise.
344        """
345        if not isinstance(other, Tree):
346            return False
347        def nodes_equal(n1, n2):
348            if n1 is None and n2 is None:
349                return True
350            if n1 is None or n2 is None:
351                return False
352            return (
353                n1.value == n2.value and
354                nodes_equal(n1.left, n2.left) and
355                nodes_equal(n1.right, n2.right)
356            )
357        return nodes_equal(self.root, other.root)
class TreeNode:
 4class TreeNode:
 5    """ 
 6    A binary tree node implementation.
 7    """
 8    def __init__(self, value, left=None, right=None):
 9        """ 
10        TreeNode constructor.
11        
12        Args:
13            value: The value of the node.
14            left: Reference to the left node.
15            right: Reference to the right node.
16        """
17        self.value = value
18        self.left = left
19        self.right = right
20
21    def copy(self):
22        """
23        Return a copy of the node.
24        """
25        new_node = TreeNode(self.value) 
26        if self.left:
27            new_node.left = self.left.copy()
28        if self.right:
29            new_node.right = self.right.copy()
30        return new_node
31    
32    def print(self, level=0):
33        """ 
34        Print the contents of a node.
35
36        Args:
37            level: starting level of node
38        """
39        if self.right:
40            self.right.print(level + 1)
41        print("   " * level + str(self.value))
42        if self.left:
43            self.left.print(level + 1)
44
45    def __lt__(self, other):
46        """
47        Compare the value of two nodes.
48        """
49        return self.value < other.value
50    
51    def __repr__(self):
52        """
53        Return the string representation of a node.
54        """
55        if self.value is None:
56            return "none"
57        else:
58            return str(self.value)

A binary tree node implementation.

TreeNode(value, left=None, right=None)
 8    def __init__(self, value, left=None, right=None):
 9        """ 
10        TreeNode constructor.
11        
12        Args:
13            value: The value of the node.
14            left: Reference to the left node.
15            right: Reference to the right node.
16        """
17        self.value = value
18        self.left = left
19        self.right = right

TreeNode constructor.

Args: value: The value of the node. left: Reference to the left node. right: Reference to the right node.

value
left
right
def copy(self):
21    def copy(self):
22        """
23        Return a copy of the node.
24        """
25        new_node = TreeNode(self.value) 
26        if self.left:
27            new_node.left = self.left.copy()
28        if self.right:
29            new_node.right = self.right.copy()
30        return new_node

Return a copy of the node.

def print(self, level=0):
32    def print(self, level=0):
33        """ 
34        Print the contents of a node.
35
36        Args:
37            level: starting level of node
38        """
39        if self.right:
40            self.right.print(level + 1)
41        print("   " * level + str(self.value))
42        if self.left:
43            self.left.print(level + 1)

Print the contents of a node.

Args: level: starting level of node

class Tree:
 60class Tree:
 61    """ 
 62    A binary search tree (BST) implementation. Can be treated as a plain binary tree if operations (insert, search, delete) are not used and nodes are set manually.
 63    """
 64    def __init__(self, root=None):
 65        """ 
 66        Args:
 67            root: Root node of the Tree.
 68        """
 69        self.root = root
 70        
 71    def search(self, value):
 72        """ 
 73        Search for a value in the binary search tree.
 74
 75        Args:
 76            value: Value to search for.
 77        
 78        Returns:
 79            node with matching value
 80
 81        Raises:
 82            ValueError: if value is not found
 83        """
 84        current = self.root
 85        
 86        while current is not None:
 87            if value == current.value:
 88                return current
 89            elif value < current.value:
 90                current = current.left
 91            elif value > current.value:
 92                current = current.right
 93
 94        raise ValueError(f"Value {value} not found in the tree")
 95
 96    def insert(self, value):
 97        """ 
 98        Insert a value in the binary search tree.
 99
100        Args:
101            value: The value to insert.
102        
103        Returns:
104            The root of the tree after deletion.
105
106        Raises:
107            ValueError: if value is already in the tree
108        """
109        self.root = self.insert_rec(self.root, value)
110        return self.root
111    
112    def insert_rec(self, root, value):
113        if root is None:
114            return TreeNode(value)
115        if value < root.value:
116            root.left = self.insert_rec(root.left, value)
117        elif value > root.value:
118            root.right = self.insert_rec(root.right, value)
119        else:
120            raise ValueError("Value already exists in the tree")
121        return root
122
123    def insert_iterative(self, value):
124        """ 
125        Insert a value in the binary search tree (iterative implementation).
126
127        Args:
128            value: The value to insert.
129        
130        Returns:
131            None
132
133        Raises:
134            ValueError: if value is already in the tree
135        """
136        current = self.root
137        if self.root is None:
138            self.root = TreeNode(value)
139            return
140        
141        while current is not None:
142            if value < current.value:
143                if current.left is None:
144                    current.left = TreeNode(value)
145                    return
146                else:
147                    current = current.left
148            elif value > current.value:
149                if current.right is None:
150                    current.right = TreeNode(value)
151                    return
152                else:
153                    current = current.right
154            else:
155                return 
156   
157    def delete(self, value):
158        """ 
159        Delete a value from the binary search tree.
160
161        Args:
162            value: The value to delete.
163        
164        Returns:
165            The root of the tree after insertion.
166
167        
168        Raises:
169            ValueError: if value is not found.
170        """
171        self.root = self.delete_node(self.root, value)
172        return self.root
173        
174    def delete_node(self, node, value):
175        """ 
176        Helper function to delete a value from the binary search tree. (Use delete() instead)
177
178        Args:
179            value: The value to delete.
180            node: The current node.
181
182        Returns:
183            The new subtree root after deletion.
184        
185        Raises:
186            ValueError: if value is not found.
187        """ 
188        if node is None:
189            raise ValueError("Value not found in the tree")
190            return None
191        
192        if value < node.value:
193            node.left = self.delete_node(node.left, value)
194        elif value > node.value:
195            node.right = self.delete_node(node.right, value)
196        else:
197            if node.left is None:
198                subtree = node.right
199                node = None
200                return subtree
201            elif node.right is None:
202                subtree = node.left
203                node = None
204                return subtree
205            
206            successor = self.successor_node(node.right)
207            node.value = successor.value
208            node.right = self.delete_node(node.right, successor.value)
209            
210        return node
211    
212    def successor_node(self, node=None):
213        """ 
214        Return the successor node (the minimum value in a binary search tree's right subtree).
215
216        Args:
217            node: The starting node.
218        
219        Returns:
220            node with the lowest value in the BST
221            None if not found
222
223        Raises:
224            ValueError: if value is not found.
225        """
226        if node is None:
227            node = self.root
228        
229        if node.left is None:
230            return node
231        else:
232            return self.successor_node(node.left)
233    
234    def predecessor_node(self, node=None):
235        """ 
236        Return the predecessor node (the maximum value in a binary search tree's left subtree).
237
238        Args:
239            node: The starting node.
240        
241        Returns:
242            node with the lowest value in the BST
243            None if not found
244
245        Raises:
246            ValueError: if value is not found.
247        """
248        if node is None:
249            node = self.root
250        
251        if node.right is None:
252            return node
253        else:
254            return self.predecessor_node(node.right)
255    
256    def delete_iterative(self, root, value):
257        """ 
258        Delete a value from the binary search tree (iterative version).
259
260        Args:
261            value: The value to delete.
262        
263        Returns:
264            The root of the tree after insertion.
265
266        
267        Raises:
268            ValueError: if value is not found.
269        """
270        parent = None
271        current = root
272
273        # Find the node to delete and its parent
274        while current and current.key != value:
275            parent = current
276            if value < current.key:
277                current = current.left
278            else:
279                current = current.right
280
281        if current is None:
282            return root  # Key not found
283
284        # Case 1 & 2: Node has at most one child
285        def replace_node_in_parent(new_child):
286            if parent is None:
287                return new_child  # Deleting the root node
288            if parent.left == current:
289                parent.left = new_child
290            else:
291                parent.right = new_child
292            return root
293
294        if current.left is None:
295            return replace_node_in_parent(current.right)
296        elif current.right is None:
297            return replace_node_in_parent(current.left)
298
299        # Case 3: Node has two children
300        # Find in-order successor and its parent
301        successor_parent = current
302        successor = current.right
303        while successor.left:
304            successor_parent = successor
305            successor = successor.left
306
307        # Replace current's key with successor's key
308        current.key = successor.key
309
310        # Delete successor node (which has at most one child)
311        if successor_parent.left == successor:
312            successor_parent.left = successor.right
313        else:
314            successor_parent.right = successor.right
315
316        return root
317
318    def print(self):
319        """ 
320        Print the values in the BST.
321        """
322        self.root.print()
323        
324    def __len__(self):
325        """ 
326        Return the number of nodes in the BST.
327        
328        Returns:
329            The number of nodes in the BST.
330        """
331        def count_nodes(node):
332            if node is None:
333                return 0
334            return 1 + count_nodes(node.left) + count_nodes(node.right)
335        
336        return count_nodes(self.root)
337        
338
339    def __eq__(self, other):
340        """
341        Compare two Tree objects for value-based equality (structure and values).
342
343        Returns:
344            True if both are Tree instances and their structures and values are equal, False otherwise.
345        """
346        if not isinstance(other, Tree):
347            return False
348        def nodes_equal(n1, n2):
349            if n1 is None and n2 is None:
350                return True
351            if n1 is None or n2 is None:
352                return False
353            return (
354                n1.value == n2.value and
355                nodes_equal(n1.left, n2.left) and
356                nodes_equal(n1.right, n2.right)
357            )
358        return nodes_equal(self.root, other.root)

A binary search tree (BST) implementation. Can be treated as a plain binary tree if operations (insert, search, delete) are not used and nodes are set manually.

Tree(root=None)
64    def __init__(self, root=None):
65        """ 
66        Args:
67            root: Root node of the Tree.
68        """
69        self.root = root

Args: root: Root node of the Tree.

root
def search(self, value):
71    def search(self, value):
72        """ 
73        Search for a value in the binary search tree.
74
75        Args:
76            value: Value to search for.
77        
78        Returns:
79            node with matching value
80
81        Raises:
82            ValueError: if value is not found
83        """
84        current = self.root
85        
86        while current is not None:
87            if value == current.value:
88                return current
89            elif value < current.value:
90                current = current.left
91            elif value > current.value:
92                current = current.right
93
94        raise ValueError(f"Value {value} not found in the tree")

Search for a value in the binary search tree.

Args: value: Value to search for.

Returns: node with matching value

Raises: ValueError: if value is not found

def insert(self, value):
 96    def insert(self, value):
 97        """ 
 98        Insert a value in the binary search tree.
 99
100        Args:
101            value: The value to insert.
102        
103        Returns:
104            The root of the tree after deletion.
105
106        Raises:
107            ValueError: if value is already in the tree
108        """
109        self.root = self.insert_rec(self.root, value)
110        return self.root

Insert a value in the binary search tree.

Args: value: The value to insert.

Returns: The root of the tree after deletion.

Raises: ValueError: if value is already in the tree

def insert_rec(self, root, value):
112    def insert_rec(self, root, value):
113        if root is None:
114            return TreeNode(value)
115        if value < root.value:
116            root.left = self.insert_rec(root.left, value)
117        elif value > root.value:
118            root.right = self.insert_rec(root.right, value)
119        else:
120            raise ValueError("Value already exists in the tree")
121        return root
def insert_iterative(self, value):
123    def insert_iterative(self, value):
124        """ 
125        Insert a value in the binary search tree (iterative implementation).
126
127        Args:
128            value: The value to insert.
129        
130        Returns:
131            None
132
133        Raises:
134            ValueError: if value is already in the tree
135        """
136        current = self.root
137        if self.root is None:
138            self.root = TreeNode(value)
139            return
140        
141        while current is not None:
142            if value < current.value:
143                if current.left is None:
144                    current.left = TreeNode(value)
145                    return
146                else:
147                    current = current.left
148            elif value > current.value:
149                if current.right is None:
150                    current.right = TreeNode(value)
151                    return
152                else:
153                    current = current.right
154            else:
155                return 

Insert a value in the binary search tree (iterative implementation).

Args: value: The value to insert.

Returns: None

Raises: ValueError: if value is already in the tree

def delete(self, value):
157    def delete(self, value):
158        """ 
159        Delete a value from the binary search tree.
160
161        Args:
162            value: The value to delete.
163        
164        Returns:
165            The root of the tree after insertion.
166
167        
168        Raises:
169            ValueError: if value is not found.
170        """
171        self.root = self.delete_node(self.root, value)
172        return self.root

Delete a value from the binary search tree.

Args: value: The value to delete.

Returns: The root of the tree after insertion.

Raises: ValueError: if value is not found.

def delete_node(self, node, value):
174    def delete_node(self, node, value):
175        """ 
176        Helper function to delete a value from the binary search tree. (Use delete() instead)
177
178        Args:
179            value: The value to delete.
180            node: The current node.
181
182        Returns:
183            The new subtree root after deletion.
184        
185        Raises:
186            ValueError: if value is not found.
187        """ 
188        if node is None:
189            raise ValueError("Value not found in the tree")
190            return None
191        
192        if value < node.value:
193            node.left = self.delete_node(node.left, value)
194        elif value > node.value:
195            node.right = self.delete_node(node.right, value)
196        else:
197            if node.left is None:
198                subtree = node.right
199                node = None
200                return subtree
201            elif node.right is None:
202                subtree = node.left
203                node = None
204                return subtree
205            
206            successor = self.successor_node(node.right)
207            node.value = successor.value
208            node.right = self.delete_node(node.right, successor.value)
209            
210        return node

Helper function to delete a value from the binary search tree. (Use delete() instead)

Args: value: The value to delete. node: The current node.

Returns: The new subtree root after deletion.

Raises: ValueError: if value is not found.

def successor_node(self, node=None):
212    def successor_node(self, node=None):
213        """ 
214        Return the successor node (the minimum value in a binary search tree's right subtree).
215
216        Args:
217            node: The starting node.
218        
219        Returns:
220            node with the lowest value in the BST
221            None if not found
222
223        Raises:
224            ValueError: if value is not found.
225        """
226        if node is None:
227            node = self.root
228        
229        if node.left is None:
230            return node
231        else:
232            return self.successor_node(node.left)

Return the successor node (the minimum value in a binary search tree's right subtree).

Args: node: The starting node.

Returns: node with the lowest value in the BST None if not found

Raises: ValueError: if value is not found.

def predecessor_node(self, node=None):
234    def predecessor_node(self, node=None):
235        """ 
236        Return the predecessor node (the maximum value in a binary search tree's left subtree).
237
238        Args:
239            node: The starting node.
240        
241        Returns:
242            node with the lowest value in the BST
243            None if not found
244
245        Raises:
246            ValueError: if value is not found.
247        """
248        if node is None:
249            node = self.root
250        
251        if node.right is None:
252            return node
253        else:
254            return self.predecessor_node(node.right)

Return the predecessor node (the maximum value in a binary search tree's left subtree).

Args: node: The starting node.

Returns: node with the lowest value in the BST None if not found

Raises: ValueError: if value is not found.

def delete_iterative(self, root, value):
256    def delete_iterative(self, root, value):
257        """ 
258        Delete a value from the binary search tree (iterative version).
259
260        Args:
261            value: The value to delete.
262        
263        Returns:
264            The root of the tree after insertion.
265
266        
267        Raises:
268            ValueError: if value is not found.
269        """
270        parent = None
271        current = root
272
273        # Find the node to delete and its parent
274        while current and current.key != value:
275            parent = current
276            if value < current.key:
277                current = current.left
278            else:
279                current = current.right
280
281        if current is None:
282            return root  # Key not found
283
284        # Case 1 & 2: Node has at most one child
285        def replace_node_in_parent(new_child):
286            if parent is None:
287                return new_child  # Deleting the root node
288            if parent.left == current:
289                parent.left = new_child
290            else:
291                parent.right = new_child
292            return root
293
294        if current.left is None:
295            return replace_node_in_parent(current.right)
296        elif current.right is None:
297            return replace_node_in_parent(current.left)
298
299        # Case 3: Node has two children
300        # Find in-order successor and its parent
301        successor_parent = current
302        successor = current.right
303        while successor.left:
304            successor_parent = successor
305            successor = successor.left
306
307        # Replace current's key with successor's key
308        current.key = successor.key
309
310        # Delete successor node (which has at most one child)
311        if successor_parent.left == successor:
312            successor_parent.left = successor.right
313        else:
314            successor_parent.right = successor.right
315
316        return root

Delete a value from the binary search tree (iterative version).

Args: value: The value to delete.

Returns: The root of the tree after insertion.

Raises: ValueError: if value is not found.

def print(self):
318    def print(self):
319        """ 
320        Print the values in the BST.
321        """
322        self.root.print()

Print the values in the BST.