Transforming a binary tree (BT) into a singly linked list (SLL) is simple, as it involves traversing the tree in in-order and adding the nodes to the linked list. This can be achieved either recursively or iteratively. Using a stack for iterative in-order traversal simplifies managing the resulting list, but in this post, I will focus on the recursive approach, which takes a bit more effort to grasp.
classSolution {
public ListNode flatten(TreeNode root, ListNode tail) {
if (root ==null) {
return tail; // Return current tail if root is null }
// Recursively flatten the left subtree, updating the tail tail = flatten(root.left, tail);
// Add current root node to the linked list tail.next=new ListNode(root.val);
tail = tail.next; // Update the tail to the newly added node// Recursively flatten the right subtree, updating the tailreturn flatten(root.right, tail);
}
// Public method to start flattening with a dummy head nodepublic ListNode convertToLinkedList(TreeNode root) {
ListNode dummy =new ListNode(-1); // Create a dummy head node for simplicity flatten(root, dummy); // Pass dummy node as the initial tailreturn dummy.next; // Return the linked list starting after dummy node }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
classSolution:
defflatten(self, root, tail):
ifnot root:
return tail # Return current tail if root is None# Flatten the left subtree and update the tail tail = self.flatten(root.left, tail)
# Add current root node to linked list tail.next = self.ListNode(root.val)
tail = tail.next # Update tail to the newly created node# Flatten the right subtree and update the tailreturn self.flatten(root.right, tail)
defconvert_to_linked_list(self, root):
dummy = self.ListNode(-1) # Create a dummy head for simplicity self.flatten(root, dummy) # Start flattening with the dummy node as the tailreturn dummy.next # Return the linked list starting from dummy.next
⏰ Time complexity: O(n). Each node is visited during traversal.
🧺 Space complexity: O(n) Stack space used for recursion, where h is the tree height, which in case of skewed tree is O(n). And we also need to return the converted list, for that we need O(n) space.
classSolution {
private TreeNode prev =null; // To keep track of the previous node in traversalpublicvoidflatten(TreeNode root) {
if (root ==null) return;
// Flatten the left subtree flatten(root.left);
// Process the current nodeif (prev !=null) {
prev.right= root; // Update the right pointer of previous node prev.left=null; // Remove the left pointer for linked list }
prev = root; // Update prev to current node// Flatten the right subtree flatten(root.right);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
classSolution:
def __init__(self):
self.prev =None# To keep track of the previous node in traversaldefflatten(self, root):
ifnot root:
return# Flatten the left subtree self.flatten(root.left)
# Process the current nodeif self.prev:
self.prev.right = root # Update the right pointer of prev node self.prev.left =None# Remove the left pointer for linked list self.prev = root # Update prev to current node# Flatten the right subtree self.flatten(root.right)
How can the binary tree be flattened in-place without relying on additional data structures to hold the list? Once a node is visited, its left and right pointers are no longer required for further traversal. These pointers can be repurposed to form the next and prev links of the linked list. For instance, the right pointer can serve as the next link in the flattened list.
classSolution {
// Definition for binary tree nodepublicstaticclassTreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val= val;
}
}
publicvoidflatten(TreeNode root) {
if (root ==null) return;
Stack<TreeNode> stack =new Stack<>();
stack.push(root);
TreeNode prev =null;
while (!stack.isEmpty()) {
TreeNode current = stack.pop();
// Process the current nodeif (prev !=null) {
prev.right= current; // Update previous node's right pointer prev.left=null; // Remove the left pointer }
// Push right and left children into the stack (right first)if (current.right!=null) stack.push(current.right);
if (current.left!=null) stack.push(current.left);
prev = current; // Update prev to the current node }
}
}
classSolution:
# Definition for binary tree nodeclassTreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
defflatten(self, root):
ifnot root:
return stack = [root]
prev =Nonewhile stack:
current = stack.pop()
# Process the current nodeif prev:
prev.right = current # Update previous node's right pointer prev.left =None# Remove the left pointer# Push right and left children into the stack (right first)if current.right:
stack.append(current.right)
if current.left:
stack.append(current.left)
⏰ Time complexity: O(n). Each node is processed exactly once.
🧺 Space complexity: O(h). Where h is the maximum depth of the tree, corresponding to the stack size. In the worst case, for a skewed tree, this is O(n).
Tree traversal can be performed in any order without using a recursion stack or an explicit stack, achieving constant space complexity. Using the Morris Inorder Traversal method discussed earlier, the same mechanism can be applied while appending the visited nodes during traversal. The implementation works in O(n) time and O(1) extra space, excluding the space needed for the returned linked list. In the future, we will explore how this can be done directly in place.
classSolution {
publicvoidflatten(TreeNode root) {
TreeNode current = root;
while (current !=null) {
if (current.left!=null) {
// Find predecessor (rightmost node in the left subtree) TreeNode predecessor = current.left;
while (predecessor.right!=null&& predecessor.right!= current) {
predecessor = predecessor.right;
}
if (predecessor.right==null) {
// Establish temporary link predecessor.right= current;
current = current.left;
} else {
// Restore tree structure predecessor.right=null;
// Process the current node TreeNode temp = current.left;
current.left=null;
current.right= temp;
current = current.right;
}
} else {
current = current.right; // Move to the right subtree }
}
}
}
classSolution:
defflatten(self, root):
current = root
while current:
if current.left:
# Find predecessor (rightmost node in the left subtree) predecessor = current.left
while predecessor.right and predecessor.right != current:
predecessor = predecessor.right
ifnot predecessor.right:
# Establish temporary link predecessor.right = current
current = current.left
else:
# Restore tree structure predecessor.right =None# Process current node temp = current.left
current.left =None current.right = temp
current = current.right
else:
current = current.right