Given the root
of a binary tree, flatten the tree into a "linked list":
TreeNode
class where the right
child pointer points to the next node in the list and the left
child pointer is always null
.For example:
Consider the following binary tree:
1
/ \
2 5
/ \ \
3 4 6
The flattened tree should be:
1
\
2
\
3
\
4
\
5
\
6
That is, the tree is transformed into a linked list by modifying the right
pointers to point to the next node in the pre-order traversal, and the left
pointers are set to null
.
Here are a few test cases to consider:
root = []
should result in []
.root = [0]
should result in [0]
.Write a function to flatten a given binary tree in-place.
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force way to flatten a tree to a linked list involves exploring all possible tree traversals. We essentially try every possible order to line up the nodes. Finally, we pick one of those orders to become our linked list.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def flatten_binary_tree_brute_force(root):
all_nodes = []
def get_all_nodes(node):
if not node:
return
all_nodes.append(node)
get_all_nodes(node.left)
get_all_nodes(node.right)
# Collect all nodes from the tree.
get_all_nodes(root)
import itertools
# Attempt every possible permutation.
for permutation in itertools.permutations(all_nodes):
# We create a linked list based on a particular permutation.
for i in range(len(permutation) - 1):
permutation[i].left = None
permutation[i].right = permutation[i+1]
if permutation:
permutation[-1].left = None
permutation[-1].right = None
if permutation:
# Return the 'head' of the flattened list
return permutation[0]
return None
The goal is to rearrange the tree so it looks like a linked list, where each node's 'right' points to the next node in the list, and the 'left' child is always empty. The clever trick is to work from the bottom up, solving smaller subproblems first and connecting them as we move upwards.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, value=0, left=None, right=None):
self.value = value
self.left = left
self.right = right
def flatten_binary_tree(root):
if not root:
return None
def flatten_subtree(node):
if not node:
return None
left_subtree_tail = flatten_subtree(node.left)
right_subtree_tail = flatten_subtree(node.right)
# Store the right subtree before modification
original_right_subtree = node.right
# Move the left subtree to the right
if node.left:
node.right = node.left
node.left = None
# Connect tail of the left to original right
if left_subtree_tail:
left_subtree_tail.right = original_right_subtree
# Find the tail of the flattened tree
tail = right_subtree_tail if right_subtree_tail else left_subtree_tail if left_subtree_tail else node
return tail
flatten_subtree(root)
Case | How to Handle |
---|---|
Null root (empty tree) | The function should return immediately without any modification if the root is null. |
Tree with only a root node | The root's left should be null and right should remain null (already flattened). |
Tree with only a left child | The left child should become the right child, and the left child should be set to null. |
Tree with only a right child | The right child should remain as the right child, and the left child should be set to null. |
Skewed tree (all nodes on one side) | The algorithm should still flatten the tree correctly in preorder, regardless of skewness, by sequentially attaching nodes to the right. |
Large tree (potential stack overflow with recursion) | Consider an iterative approach to avoid stack overflow errors with deep trees. |
Tree with duplicate values | The preorder traversal based flattening works independently of node values, so duplicates do not affect the correctness. |
Memory constraints with very large trees | In-place modification is crucial to adhere to memory constraints, and iterative implementations might offer better memory management. |