On this page
Description
Flattening turns a binary tree into a linked list without creating a single new node. Each node's right pointer becomes "next", and its left pointer is either cleared or reused as "previous". Interviews ask for two versions: a singly linked list in preorder, and a doubly linked list in inorder. The tricky part is order. A node can only point at the rest of the list once that rest exists.
We'll go from simple to more technical:
- The pattern: build the preorder list backwards, from its last node to its first.
- A second flavor: a doubly linked list in inorder.
- Where in-place flattening helps.
- The bugs: building in the wrong order and leaving old
leftpointers behind.
The approach
In the finished list, every node points at the node after it in preorder. The easy way to get that right is to build the list from its end. Visit the nodes in the exact reverse of preorder: right subtree, then left subtree, then the node itself. Keep prev, the first node of the part you've already built. Each node sets right = prev, clears left, and becomes the new prev.
Take this tree. Its preorder is 8, 3, 1, 6, 10, 9, 14.
The walk links the nodes in reverse preorder. Each one goes in front of the chain built so far:
| Step | Node linked | Chain after the step |
|---|---|---|
| 1 | 14 | 14 |
| 2 | 9 | 9 → 14 |
| 3 | 10 | 10 → 9 → 14 |
| 4 | 6 | 6 → 10 → 9 → 14 |
| 5 | 1 | 1 → 6 → 10 → 9 → 14 |
| 6 | 3 | 3 → 1 → 6 → 10 → 9 → 14 |
| 7 | 8 | 8 → 3 → 1 → 6 → 10 → 9 → 14 |
Java
TreeNode prev = null;
void flatten(TreeNode root) {
if (root == null) return;
flatten(root.right); // flatten right first...
flatten(root.left); // ...then left...
root.right = prev; // ...so `prev` is always the correctly-flattened chain so far
root.left = null;
prev = root;
}
Python
class Solution:
def __init__(self):
self.prev = None
def flatten(self, root: TreeNode | None) -> None:
if root is None:
return
self.flatten(root.right) # flatten right first...
self.flatten(root.left) # ...then left...
root.right = self.prev # ...so self.prev is always the correctly-flattened chain so far
root.left = None
self.prev = root
The first node to be linked is the last one in preorder (14). Its prev is still null, which is right: nothing comes after it. Every later node plugs itself in front. When the recursion gets back to the root, the whole list reads in preorder.
A second flavor: a doubly linked list in inorder
Now the list should follow inorder (1, 3, 6, 8, 9, 10, 14 for the tree above), with links both ways. right means "next" and left means "previous". This time the list is built from the front. Walk in ordinary inorder: left subtree, node, right subtree. prev is the node you visited just before this one. Link the two in both directions and move on. The first node you visit has no prev, so it becomes the head.
Java
TreeNode prev = null, head = null;
void convert(TreeNode node) {
if (node == null) return;
convert(node.left);
if (prev == null) {
head = node; // the first node visited in inorder becomes the list's head
} else {
prev.right = node; // link the previous node's "next" forward to this one
node.left = prev; // link this node's "previous" backward to the previous one
}
prev = node;
convert(node.right);
}
Python
def tree_to_doubly_list(root: TreeNode | None) -> TreeNode | None:
prev = head = None
def convert(node: TreeNode | None) -> None:
nonlocal prev, head
if node is None:
return
convert(node.left)
if prev is None:
head = node # the first node visited in inorder becomes the list's head
else:
prev.right = node # link the previous node's "next" forward to this one
node.left = prev # link this node's "previous" backward to the previous one
prev = node
convert(node.right)
convert(root)
return head
Changing a node's left here is safe. By the time the walk reaches a node, its left subtree is already finished.
Where you'll see it
Flattening helps when you need a tree as a sequence and don't want to copy it. A classic use is merging two binary search trees. Flatten each one into a sorted list, merge the lists, then build a balanced tree from the result.
Which tool for which job?
| You need | Use |
|---|---|
| Preorder as a right-pointer list | reverse preorder (right, left, node) linking to prev |
| The same with O(1) extra space | at each node, move its left subtree in between the node and its right child, then step right (no recursion) |
| A sorted doubly linked list from a BST | inorder, linking prev both ways |
| Merge two BSTs | flatten both to sorted lists, merge, rebuild balanced |
| The original tree kept | build a new list instead |
Common mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
Flattening left before right with prev | the right subtree's head isn't ready | right, then left, then the node |
Not clearing left | stale links; not a clean list | set left = null |
A prev field kept across calls | the next call links to the old tree | reset it per call |
Only next links in the doubly linked version | backward walks break | link both directions |
| Recursion on a very deep tree | stack overflow | the O(1)-space splice |
Check yourself
Try to answer before opening each one - recalling beats re-reading.
1. Preorder visits left before right. So why does the flatten walk do the right subtree first?
Show answer
Each node points at the part of the list that comes after it. In preorder, that's the left subtree followed by the right subtree. So the right subtree's chain has to exist first, then the left one in front of it, then the node. Right, then left, then the node builds the list from its end.
2. After root.right = prev, why set root.left to null too?
Show answer
root.left still points at the old left child, which is now further down the list. A walk that only follows right would still work. But the result isn't a clean list: the node still looks like it has a left child, and anything that reads left will go wrong.
3. Why does the doubly linked version use plain inorder, while flatten uses the reverse order?
Show answer
The doubly linked version builds the list from the front. Each node links back to the node visited just before it, so the normal visiting order works. Flatten puts each node in front of a chain that must already exist, so it has to build from the end.
Key takeaways
- Flatten to preorder by visiting right, left, then the node, and linking each node in front of
prev. - After setting
right, clearleft, or the list keeps a stale pointer. - The doubly linked version walks in plain inorder and links each node to the one before it, both ways.
- Both reuse the tree's own pointers and create no new nodes.