1- from __future__ import annotations
1+ from __future__ import annotations # Allows using the class itself as a type hint within the class
22
3- from collections .abc import Iterator
4- from dataclasses import dataclass
3+ from collections .abc import Iterator # For type hinting iteration
4+ from dataclasses import dataclass # For concise class definitions
55
66
7+ # -----------------------------
8+ # Node class: represents one node in the binary tree
9+ # -----------------------------
710@dataclass
811class Node :
9- data : int
10- left : Node | None = None
11- right : Node | None = None
12-
12+ data : int # Value stored in the node
13+ left : Node | None = None # Left child node
14+ right : Node | None = None # Right child node
15+
16+ # -----------------------------
17+ # In-order traversal generator
18+ # Allows iterating over Node like: `for x in node: ...`
19+ # -----------------------------
1320 def __iter__ (self ) -> Iterator [int ]:
1421 if self .left :
15- yield from self .left
16- yield self .data
22+ yield from self .left # Yield all values from the left subtree
23+ yield self .data # Yield the current node's data
1724 if self .right :
18- yield from self .right
25+ yield from self .right # Yield all values from the right subtree
1926
27+ # -----------------------------
28+ # Returns number of nodes in the subtree rooted at this node
29+ # -----------------------------
2030 def __len__ (self ) -> int :
21- return sum (1 for _ in self )
31+ return sum (1 for _ in self ) # Count all nodes using iteration
2232
33+ # -----------------------------
34+ # Check if the subtree rooted at this node is a full binary tree
35+ # A full binary tree: every node has 0 or 2 children
36+ # -----------------------------
2337 def is_full (self ) -> bool :
2438 if not self or (not self .left and not self .right ):
25- return True
39+ return True # Leaf node is full
2640 if self .left and self .right :
41+ # Node has two children → recursively check both subtrees
2742 return self .left .is_full () and self .right .is_full ()
28- return False
43+ return False # Node has only one child → not full
2944
3045
46+ # -----------------------------
47+ # BinaryTree class: represents the whole binary tree
48+ # -----------------------------
3149@dataclass
3250class BinaryTree :
33- root : Node
51+ root : Node # Root node of the tree
3452
53+ # -----------------------------
54+ # Iterate over tree using in-order traversal
55+ # -----------------------------
3556 def __iter__ (self ) -> Iterator [int ]:
3657 return iter (self .root )
3758
59+ # -----------------------------
60+ # Return total number of nodes in the tree
61+ # -----------------------------
3862 def __len__ (self ) -> int :
3963 return len (self .root )
4064
65+ # -----------------------------
66+ # Factory method: small tree (3 nodes)
67+ # -----------------------------
4168 @classmethod
4269 def small_tree (cls ) -> BinaryTree :
4370 """
@@ -48,22 +75,25 @@ def small_tree(cls) -> BinaryTree:
4875 >>> list(binary_tree)
4976 [1, 2, 3]
5077 """
51- binary_tree = BinaryTree (Node (2 ))
52- binary_tree .root .left = Node (1 )
53- binary_tree .root .right = Node (3 )
78+ binary_tree = BinaryTree (Node (2 )) # Root node = 2
79+ binary_tree .root .left = Node (1 ) # Left child
80+ binary_tree .root .right = Node (3 ) # Right child
5481 return binary_tree
5582
83+ # -----------------------------
84+ # Factory method: medium tree (7 nodes)
85+ # -----------------------------
5686 @classmethod
5787 def medium_tree (cls ) -> BinaryTree :
5888 """
59- Return a medium binary tree with 3 nodes.
89+ Return a medium binary tree with 7 nodes.
6090 >>> binary_tree = BinaryTree.medium_tree()
6191 >>> len(binary_tree)
6292 7
6393 >>> list(binary_tree)
6494 [1, 2, 3, 4, 5, 6, 7]
6595 """
66- binary_tree = BinaryTree (Node (4 ))
96+ binary_tree = BinaryTree (Node (4 )) # Root node = 4
6797 binary_tree .root .left = two = Node (2 )
6898 two .left = Node (1 )
6999 two .right = Node (3 )
@@ -72,10 +102,12 @@ def medium_tree(cls) -> BinaryTree:
72102 six .right = Node (7 )
73103 return binary_tree
74104
105+ # -----------------------------
106+ # Public method: get depth of tree
107+ # -----------------------------
75108 def depth (self ) -> int :
76109 """
77110 Returns the depth of the tree
78-
79111 >>> BinaryTree(Node(1)).depth()
80112 1
81113 >>> BinaryTree.small_tree().depth()
@@ -85,15 +117,21 @@ def depth(self) -> int:
85117 """
86118 return self ._depth (self .root )
87119
120+ # -----------------------------
121+ # Helper recursive method to compute depth
122+ # Depth = 1 + max(depth of left, depth of right)
123+ # -----------------------------
88124 def _depth (self , node : Node | None ) -> int :
89125 if not node :
90- return 0
126+ return 0 # Empty node contributes 0 to depth
91127 return 1 + max (self ._depth (node .left ), self ._depth (node .right ))
92128
129+ # -----------------------------
130+ # Check if the tree is full
131+ # -----------------------------
93132 def is_full (self ) -> bool :
94133 """
95134 Returns True if the tree is full
96-
97135 >>> BinaryTree(Node(1)).is_full()
98136 True
99137 >>> BinaryTree.small_tree().is_full()
@@ -104,7 +142,10 @@ def is_full(self) -> bool:
104142 return self .root .is_full ()
105143
106144
145+ # -----------------------------
146+ # Run doctests if executed as main
147+ # -----------------------------
107148if __name__ == "__main__" :
108149 import doctest
109150
110- doctest .testmod ()
151+ doctest .testmod () # Automatically tests all docstring examples
0 commit comments