Repository: TheAlgorithms/Python

Issue:
Making binary tree traversals lazy.
### Feature description

Hi,
In [binary_tree_traversals.py](https://github.com/TheAlgorithms/Python/blob/master/data_structures/binary_tree/binary_tree_traversals.py) file, there are different kinds of traversals such as `preorder`, `inorder`, `postorder` and etc.

Although the implementations are pretty clean one-liner like:
```python
# preorder
return [root.data, *preorder(root.left), *preorder(root.right)] if root else []
```
It isn't memory friendly. We can use generators instead not to load all the nodes into the memory:

```python
# preorder
    if not root:
        return []
    yield root.data
    yield from preorder(root.left)
    yield from preorder(root.right)
```
Shall we go ahead and change them?


Discussion:
+1
Absolutely, it's a great idea to switch from list-based traversals to generator-based traversals in the `binary_tree_traversals.py` file. Using generators instead of lists will indeed make the traversals more memory-friendly, especially for large trees.
@amirsoroush  Please assign this task to me.
@digital-dev-07 Read the [contributing guidelines](https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md#contribution).
> If you are interested in resolving an [open issue](https://github.com/TheAlgorithms/Python/issues), simply make a pull request with your proposed fix. We do not assign issues in this repo so please do not ask for permission to work on an issue.

Resolving patch:
diff --git a/data_structures/binary_tree/binary_tree_traversals.py b/data_structures/binary_tree/binary_tree_traversals.py
--- a/data_structures/binary_tree/binary_tree_traversals.py
+++ b/data_structures/binary_tree/binary_tree_traversals.py
@@ -1,9 +1,8 @@
 from __future__ import annotations
 
 from collections import deque
-from collections.abc import Generator, Sequence
+from collections.abc import Generator
 from dataclasses import dataclass
-from typing import Any
 
 
 # https://en.wikipedia.org/wiki/Tree_traversal
@@ -94,96 +93,86 @@ def height(root: Node | None) -> int:
     return (max(height(root.left), height(root.right)) + 1) if root else 0
 
 
-def level_order(root: Node | None) -> Sequence[Node | None]:
+def level_order(root: Node | None) -> Generator[int, None, None]:
     """
     Returns a list of nodes value from a whole binary tree in Level Order Traverse.
     Level Order traverse: Visit nodes of the tree level-by-level.
     """
-    output: list[Any] = []
 
     if root is None:
-        return output
+        return
 
     process_queue = deque([root])
 
     while process_queue:
         node = process_queue.popleft()
-        output.append(node.data)
+        yield node.data
 
         if node.left:
             process_queue.append(node.left)
         if node.right:
             process_queue.append(node.right)
-    return output
 
 
 def get_nodes_from_left_to_right(
     root: Node | None, level: int
-) -> Sequence[Node | None]:
+) -> Generator[int, None, None]:
     """
     Returns a list of nodes value from a particular level:
     Left to right direction of the binary tree.
     """
-    output: list[Any] = []
 
-    def populate_output(root: Node | None, level: int) -> None:
+    def populate_output(root: Node | None, level: int) -> Generator[int, None, None]:
         if not root:
             return
         if level == 1:
-            output.append(root.data)
+            yield root.data
         elif level > 1:
-            populate_output(root.left, level - 1)
-            populate_output(root.right, level - 1)
+            yield from populate_output(root.left, level - 1)
+            yield from populate_output(root.right, level - 1)
 
-    populate_output(root, level)
-    return output
+    yield from populate_output(root, level)
 
 
 def get_nodes_from_right_to_left(
     root: Node | None, level: int
-) -> Sequence[Node | None]:
+) -> Generator[int, None, None]:
     """
     Returns a list of nodes value from a particular level:
     Right to left direction of the binary tree.
     """
-    output: list[Any] = []
 
-    def populate_output(root: Node | None, level: int) -> None:
+    def populate_output(root: Node | None, level: int) -> Generator[int, None, None]:
         if root is None:
             return
         if level == 1:
-            output.append(root.data)
+            yield root.data
         elif level > 1:
-            populate_output(root.right, level - 1)
-            populate_output(root.left, level - 1)
+            yield from populate_output(root.right, level - 1)
+            yield from populate_output(root.left, level - 1)
 
-    populate_output(root, level)
-    return output
+    yield from populate_output(root, level)
 
 
-def zigzag(root: Node | None) -> Sequence[Node | None] | list[Any]:
+def zigzag(root: Node | None) -> Generator[int, None, None]:
     """
     ZigZag traverse:
     Returns a list of nodes value from left to right and right to left, alternatively.
     """
     if root is None:
-        return []
-
-    output: list[Sequence[Node | None]] = []
+        return
 
     flag = 0
     height_tree = height(root)
 
     for h in range(1, height_tree + 1):
         if not flag:
-            output.append(get_nodes_from_left_to_right(root, h))
+            yield from get_nodes_from_left_to_right(root, h)
             flag = 1
         else:
-            output.append(get_nodes_from_right_to_left(root, h))
+            yield from get_nodes_from_right_to_left(root, h)
             flag = 0
 
-    return output
-
 
 def main() -> None:  # Main function for testing.
     # Create binary tree.
@@ -198,15 +187,15 @@ def main() -> None:  # Main function for testing.
     print(f"Height of Tree: {height(root)}", "\n")
 
     print("Complete Level Order Traversal: ")
-    print(level_order(root), "\n")
+    print(f"{list(level_order(root))} \n")
 
     print("Level-wise order Traversal: ")
 
     for level in range(1, height(root) + 1):
-        print(f"Level {level}:", get_nodes_from_left_to_right(root, level=level))
+        print(f"Level {level}:", list(get_nodes_from_left_to_right(root, level=level)))
 
     print("\nZigZag order Traversal: ")
-    print(zigzag(root))
+    print(f"{list(zigzag(root))}")
 
 
 if __name__ == "__main__":


Test patch: