Recursion on Trees in Python
Last Updated :
16 Apr, 2024
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 (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)
OutputDepth-First Search (DFS) Recursive:
1
2
4
5
3
6
7
Auxiliary Space: O(n)
Time Complexity: O(n)
- 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))
OutputHeight 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))
OutputSize of the tree: 7
Auxiliary Space: O(n)
Time Complexity: O(n)
- 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))
OutputMaximum element in the tree: 8
Minimum element in the tree: 2
Auxiliary Space: O(n)
Time Complexity: O(n)
- 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))
OutputIs the symmetric tree symmetric? True
Is the non-symmetric tree symmetric? False
Auxiliary Space: O(n)
Time Complexity: O(n)
- 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.")
OutputPath to node 5: [1, 2, 5]
Auxiliary Space : O(n * h)
Time Complexity: O(n)
- 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.")
OutputLCA 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)
OutputMorris In-order Traversal:
4 2 5 1 3
Auxiliary Space: O(1)
Time Complexity: O(n)
Share your thoughts in the comments
Please Login to comment...