Flatten Binary Tree to Linked List
Rearrange the tree in place into a chain that leans entirely to the right: every node's left pointer must end up null, and its right pointer must point to the next node in the tree's original pre-order sequence. The last node's right is null. You must reuse the existing nodes and rewire their pointers rather than allocating new ones, and the same root object is the head of the resulting chain. An empty tree stays empty.
- The number of nodes in the tree is in the range [0, 2000]
- -100 <= Node.val <= 100
- The transformation must be in place — no new nodes
- Every left pointer must be null when you finish
root = [1,2,5,3,4,null,6]1 → 2 → 3 → 4 → 5 → 6, all via right pointers, all left pointers nullroot = [1,null,2,null,3]1 → 2 → 3root = [1,2,null,3]1 → 2 → 3root = [][]The target shape is a chain, and its order is the tree's pre-order sequence: the node, then everything from its left subtree, then everything from its right subtree. Since a chain has only one outgoing link per node, the left pointers must all end up null and the whole sequence must run along right pointers.
The natural first attempt is to build the chain top-down, in pre-order — visit a node, attach its left subtree to its right pointer, keep going. Try it on a node 1 with children 2 and 5:
node.right = node.left # attach the left subtree where the chain must continue
node.left = NoneThe first assignment overwrites node.right, and that pointer was the only reference to node 5 and everything beneath it. That subtree is now unreachable — we have not flattened the tree, we have deleted half of it.
This is the situation Invert Binary Tree specifically did not have. There, the swap at a node and the recursion beneath it touched disjoint links, so any order worked. Here the node's own rewiring destroys a pointer the recursion still needs. So order is not free any more: we must not overwrite a link until we no longer need what it points to.
One repair is to save the right subtree in a temporary variable, flatten both sides, then splice: run down the flattened left chain to its tail and attach the flattened right chain there. That works and is a perfectly good answer, but finding each tail costs a walk, and those walks add up to O(N²) on a badly shaped tree.
Here is the reframe. The trouble came from writing a node's link before its successors existed. So build the chain from its end, where every link we write points at something already finished.
Pre-order is node, left, right. Reverse it and you get right, left, node — visit the right subtree first, then the left, and do the node's work last. Walk the tree in that order and the nodes come up in exactly reverse pre-order: the final node of the chain first, the root last.
Now keep one variable, tail, holding the head of the portion of the chain already assembled. Every node's job is then two assignments: point right at tail, set left to null, and become the new tail. Since tail always holds nodes we have already finished with, nothing we still need is ever overwritten.
tail = None
def flatten(node):
nonlocal tail
if node is None:
return
flatten(node.right) # the far end of the chain, built first
flatten(node.left)
node.right = tail # link forward to what is already assembled
node.left = None
tail = node # this node is now the front of the assembled partnode.right is read (by the recursive call) before it is written, and that ordering is the entire trick. The call flatten(node.right) happens on the first line, while the pointer is still intact; the assignment node.right = tail happens only after both subtrees have been fully processed and no longer need their original links. Move the assignment above the calls and you are back to the broken version. When an in-place rewiring problem feels impossible, ask whether processing in the reverse of the natural order lets every write land after its last read — the same idea makes the in-place merge of two arrays work from the back.The result reads in pre-order, and every node was written exactly once.
Every node is visited once and performs two assignments: O(N) time, versus O(N²) for the splice-the-tails version on a skewed tree. Space is the recursion stack, O(H). The O(1)-space variant threads the tree iteratively — for each node with a left child, find that subtree's rightmost node, hook the node's right subtree onto it, move the left subtree over, and step right — which is the same rewiring idea that powers Morris Traversal.
The general lesson is about mutation order, and it comes up well beyond trees: when an algorithm overwrites the structure it is still traversing, the fix is usually to reverse the order of operations so that every write happens after the last read of what it overwrites. Keeping a running tail that only ever refers to finished work is the concrete form that takes here, and it is the same technique as reversing a linked list by pushing each node onto the front of an already-reversed remainder.
In-Place Flattening
Reverse Pre-Order Stitching
"To flatten without extra space, process from tail to head."
Strategy
A reverse pre-order (Right → Left → Root) visits nodes in the exact opposite order they appear in the flattened list. This allows us to point `node.right` to the `prev` node easily.
"Right → Left → Root"