A Binary Tree is a Tree DS in which every node has at most 2 child nodes meaning we can have BT node having exactly either 1 node or 2 nodes.
Types of Binary Trees:
| Not a BST since 9 is larger than 8.  It should be in the right subtree of 8.  | 
  
     
   | 
  Not a BST or TREE as it contains cycle. | 
     
   | 
| Operation | Average | Worst | 
|---|---|---|
| Insert | ||
| Delete | ||
| Remove | ||
| Search | 
BST - Search/Find Operation:
BST - Insert Operation:
BST - Remove Opertaion:
Use cases:
Tree Traversal techniques include various ways to visit all the nodes of the tree.
Preorder Traversal: (Root, Left, Right)
Approach:
Use cases:
Copying a Tree (Cloning):
Expression Trees (Prefix Notation):
Tree Serialization (Storing and Loading Trees):
File System Traversal:
Solving Maze/Graph Problems (DFS):
Network Routing (Hierarchical Networks):
Sudo code - Recursive Algo.:
    def preorder_recursive(root):
      if root:
          print(root.val, end=" ")  # Visit/Process the node
          preorder_recursive(root.left)  # Traverse left
          preorder_recursive(root.right)  # Traverse right
    def preorder_iterative(root):
      if not root:
          return
      
      stack = [root]
      while stack:
          node = stack.pop()
          print(node.val, end=" ")  # Visit the node
          if node.right:
              stack.append(node.right)  # Push right child first (LIFO)
          if node.left:
              stack.append(node.left)  # Push left child last (so it gets processed first)
Approach:
Note:
Use cases:
Binary Search Trees (BST) Sorting:
Converting a Binary Tree to a Doubly Linked List:
K-th Smallest/Largest Element in BST:
Validating a Binary Search Tree (BST):
Expression Trees (Infix Expression):
Database Queries and Indexing:
Sudo code - Recursive Algo.:
    def inorder_recursive(root):
      if root:
          inorder_recursive(root.left)  # Traverse left
          print(root.val, end=" ")  # Visit the node
          inorder_recursive(root.right)  # Traverse right
    def inorder_iterative(root):
      stack = []
      current = root
      while stack or current:
          while current:  # Go as left as possible
              stack.append(current)
              current = current.left
          current = stack.pop()  # Visit the node
          print(current.val, end=" ")
          current = current.right  # Move to right subtree
Deleting a Tree (Memory Cleanup):
Expression Tree Evaluation (Postfix Notation):
Dependency Resolution (Task Scheduling, Build Systems):
File System Deletion (Recursive Folder Deletion):
Tree Balancing (AVL Tree, Red-Black Tree):
Reverse Polish Notation (RPN) Evaluation:
    def post_order_traversal(root):
      if root:
          post_order_traversal(root.left)
          post_order_traversal(root.right)
          print(root.value, end=" ")
    def post_order_iterative(root):
      if not root:
          return
      
      stack, output = [root], []          
      while stack:
          node = stack.pop()
          output.append(node.value)
          
          if node.left:
              stack.append(node.left)
          if node.right:
              stack.append(node.right)
      print(" ".join(map(str, output[::-1])))  # Reverse output for post-order
Level-Order Traversal: (Breadth-First Search)
Level Order Traversal processes nodes level by level from top to bottom, left to right.
This traversal is implemented using a queue (FIFO structure).
Begins with root node inside the queue and finishes when the queue is empty.
At each iteration we add the left child and then the right child of current node to our queue.
Approach:
How It Works (Using a Queue)
    1. Start with the root in the queue.
    2. While the queue is not empty:
        Dequeue a node and process it.
        Enqueue its left and right children (if they exist).
    3. Repeat until all nodes are processed.
Use cases:
Finding the Shortest Path in an Unweighted Graph:
Printing a Tree Level-by-Level:
Finding the Deepest Node:
Checking If a Tree is Complete:
Serializing and Deserializing Trees:
Sudo code: implementation
    def level_order_traversal(root):
      if not root:
          return
      
      queue = deque([root])
      while queue:
          node = queue.popleft()
          print(node.value, end=" ")  # Process node
          if node.left:
              queue.append(node.left)  # Enqueue left child
          if node.right:
              queue.append(node.right) # Enqueue right child
Implementations:
References: