Open In App

Recursion on Trees in Python

Last Updated : 16 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In Python, recursion is implemented by defining a function that makes a call to itself within its definition. This process continues until a base case is reached, which is a condition where the function returns a value without making any further recursive calls. Without a base case, the recursion would continue indefinitely, leading to what’s known as “infinite recursion,” which can cause the program to crash.


Depth First Search using Recursive Algorithms on Trees in Python:

  • Depth-First Search (DFS) is a traversal algorithm that explores as far as possible along each branch before backtracking.
  • In a recursive implementation, we typically perform a depth-first traversal by recursively visiting the child nodes of each node.

Below is the implementation of the above code:

Python3
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

def dfs_recursive(node):
    if node is None:
        return
    
    # Visit the current node
    print(node.value)

    # Traverse left subtree
    dfs_recursive(node.left)

    # Traverse right subtree
    dfs_recursive(node.right)

# Example usage:
# Create a binary tree
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
root.right.left = TreeNode(6)
root.right.right = TreeNode(7)

# Perform DFS recursively
print("Depth-First Search (DFS) Recursive:")
dfs_recursive(root)

Output
Depth-First Search (DFS) Recursive:
1
2
4
5
3
6
7

Auxiliary Space: O(n)
Time Complexity: O(n)

Tree Height and Depth using Recursion in Python:

  • The height of a tree is the length of the longest path from the root node to a leaf node.
  • The depth of a node is the length of the path from the root to that node.
  • These can be calculated recursively by traversing the tree and keeping track of the depth or height as we go.

Below is the implementation of the above code:

Python3
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

def tree_height(node):
    if node is None:
        return 0
    else:
        left_height = tree_height(node.left)
        right_height = tree_height(node.right)
        return max(left_height, right_height) + 1

def tree_depth(node):
    if node is None:
        return 0
    else:
        left_depth = tree_depth(node.left)
        right_depth = tree_depth(node.right)
        return max(left_depth, right_depth) + 1

# Example usage:
# Create a binary tree
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
root.right.left = TreeNode(6)
root.right.right = TreeNode(7)

# Calculate height and depth
print("Height of the tree:", tree_height(root))
print("Depth of the tree:", tree_depth(root))

Output
Height of the tree: 3
Depth of the tree: 3

Auxiliary Space: O(n)
Time Complexity: O(n)

Tree Size using Recursion in Python:

  • The size of a tree is the total number of nodes in the tree, including the root node and all its descendants.
  • This can be calculated recursively by summing up the sizes of the left and right subtrees and adding 1 for the root node.

Below is the implementation of the above code:

Python3
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

def tree_size(node):
    if node is None:
        return 0
    else:
        left_size = tree_size(node.left)
        right_size = tree_size(node.right)
        return left_size + right_size + 1

# Example usage:
# Create a binary tree
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
root.right.left = TreeNode(6)
root.right.right = TreeNode(7)

# Calculate the size of the tree
print("Size of the tree:", tree_size(root))

Output
Size of the tree: 7

Auxiliary Space: O(n)
Time Complexity: O(n)

Finding Maximum/Minimum Node in Tree using Recursion in Python

  • To find the maximum or minimum element in a tree, we can recursively traverse the tree and compare values at each node.

Below is the implementation of the above code:

Python3
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

def find_max(node):
    if node is None:
        return float('-inf')
    else:
        max_left = find_max(node.left)
        max_right = find_max(node.right)
        return max(node.value, max_left, max_right)

def find_min(node):
    if node is None:
        return float('inf')
    else:
        min_left = find_min(node.left)
        min_right = find_min(node.right)
        return min(node.value, min_left, min_right)

# Example usage:
# Create a binary tree
root = TreeNode(5)
root.left = TreeNode(3)
root.right = TreeNode(7)
root.left.left = TreeNode(2)
root.left.right = TreeNode(4)
root.right.left = TreeNode(6)
root.right.right = TreeNode(8)

# Find maximum and minimum elements
print("Maximum element in the tree:", find_max(root))
print("Minimum element in the tree:", find_min(root))

Output
Maximum element in the tree: 8
Minimum element in the tree: 2

Auxiliary Space: O(n)
Time Complexity: O(n)

Check for Symmetry in Tree using Recursion in Python:

  • Checking for symmetry in a tree involves comparing the left and right subtrees of the root recursively.
  • At each step, we compare corresponding nodes in the left and right subtrees.

Below is the implementation of the above code:

Python3
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

def is_symmetric(left, right):
    # Base case: If both nodes are None, they are symmetric
    if left is None and right is None:
        return True
    # If only one node is None or their values are different, they are not symmetric
    if left is None or right is None or left.value != right.value:
        return False
    # Recursively check the symmetry of corresponding nodes in the left and right subtrees
    return is_symmetric(left.left, right.right) and is_symmetric(left.right, right.left)

def check_symmetry(root):
    # Empty tree is symmetric
    if root is None:
        return True
    # Check the symmetry of the left and right subtrees
    return is_symmetric(root.left, root.right)

# Example usage:
# Create a symmetric binary tree
symmetric_root = TreeNode(1)
symmetric_root.left = TreeNode(2)
symmetric_root.right = TreeNode(2)
symmetric_root.left.left = TreeNode(3)
symmetric_root.left.right = TreeNode(4)
symmetric_root.right.left = TreeNode(4)
symmetric_root.right.right = TreeNode(3)

# Create a non-symmetric binary tree
non_symmetric_root = TreeNode(1)
non_symmetric_root.left = TreeNode(2)
non_symmetric_root.right = TreeNode(2)
non_symmetric_root.left.right = TreeNode(3)
non_symmetric_root.right.right = TreeNode(3)

# Check symmetry
print("Is the symmetric tree symmetric?", check_symmetry(symmetric_root))
print("Is the non-symmetric tree symmetric?", check_symmetry(non_symmetric_root))

Output
Is the symmetric tree symmetric? True
Is the non-symmetric tree symmetric? False

Auxiliary Space: O(n)
Time Complexity: O(n)

Path Finding in Tree using Recursion in Python:

  • Path finding involves finding a path from the root of the tree to a given node.
  • We can use recursion to traverse the tree and keep track of the path.

Below is the implementation of the above code:

Python3
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

def find_path(root, target, path=None):
    if path is None:
        path = []

    if root is None:
        return []

    path.append(root.value)

    if root.value == target:
        return path

    left_path = find_path(root.left, target, path.copy())
    right_path = find_path(root.right, target, path.copy())

    if left_path:
        return left_path
    elif right_path:
        return right_path
    else:
        return []

# Example usage:
# Create a binary tree
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
root.right.left = TreeNode(6)
root.right.right = TreeNode(7)

# Find path to node 5
target_node = 5
path_to_node = find_path(root, target_node)
if path_to_node:
    print(f"Path to node {target_node}: {path_to_node}")
else:
    print(f"Node {target_node} not found in the tree.")

Output
Path to node 5: [1, 2, 5]

Auxiliary Space : O(n * h)
Time Complexity: O(n)

Finding Lowest Common Ancestor using Recursion in Python:

  • The Lowest Common Ancestor (LCA) of two nodes in a binary tree is the lowest node in the tree that has both nodes as descendants.
  • We can use recursion to find the LCA of two given nodes.

Below is the implementation of the above code:

Python3
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

def find_lca(root, node1, node2):
    if root is None:
        return None

    # If the root is one of the nodes, it is the LCA
    if root.value == node1 or root.value == node2:
        return root

    # Recursively find LCA in left and right subtrees
    left_lca = find_lca(root.left, node1, node2)
    right_lca = find_lca(root.right, node1, node2)

    # If both nodes are found in different subtrees, root is the LCA
    if left_lca and right_lca:
        return root

    # Otherwise, return the non-empty subtree
    return left_lca if left_lca else right_lca

# Example usage:
# Create a binary tree
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
root.right.left = TreeNode(6)
root.right.right = TreeNode(7)

# Find LCA of nodes 4 and 5
node1_value = 4
node2_value = 5
lca_node = find_lca(root, node1_value, node2_value)
if lca_node:
    print(f"LCA of nodes {node1_value} and {node2_value} is: {lca_node.value}")
else:
    print(f"Nodes {node1_value} and {node2_value} are not found in the tree or they don't have a common ancestor.")

Output
LCA of nodes 4 and 5 is: 2


Auxiliary Space: O(n)
Time Complexity: O(n)

Morris Traversal using Recursion in Python:

Morris Traversal is an in-order tree traversal algorithm that does not use recursion or a stack. Instead, it modifies the tree structure by linking the rightmost node of the left subtree to the current node, allowing traversal without additional space.

Below is the implementation of the above code:

Python3
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

def morris_inorder_traversal(root):
    current = root
    while current:
        if current.left is None:
            print(current.value, end=" ")
            current = current.right
        else:
            # Find the rightmost node in the left subtree
            predecessor = current.left
            while predecessor.right and predecessor.right != current:
                predecessor = predecessor.right
            
            if predecessor.right is None:
                predecessor.right = current
                current = current.left
            else:
                predecessor.right = None
                print(current.value, end=" ")
                current = current.right

# Example usage:
# Create a binary tree
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

# Perform Morris in-order traversal
print("Morris In-order Traversal:")
morris_inorder_traversal(root)

Output
Morris In-order Traversal:
4 2 5 1 3 

Auxiliary Space: O(1)
Time Complexity: O(n)



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads