Convert a Binary Tree into a Doubly Linked List
Problem and examples
| Input Tree | Level Order Output | Description |
|---|---|---|
| [10, 12, 15, 25, 30, 36] | [[10], [12, 15], [25, 30, 36]] | Standard binary tree; in-order traversal yields DLL: 25 ⇄ 12 ⇄ 30 ⇄ 10 ⇄ 36 ⇄ 15 |
| [1] | [[1]] | Single-node tree; DLL is just: 1 |
| [] | [] | Empty tree; DLL does not exist |
| [4, 3, null, 2, null, 1] | [[4], [3], [2], [1]] | Left-skewed tree; DLL follows in-order: 1 ⇄ 2 ⇄ 3 ⇄ 4 |
| [5, null, 6, null, 7, null, 8] | [[5], [6], [7], [8]] | Right-skewed tree; DLL is 5 ⇄ 6 ⇄ 7 ⇄ 8 |
How it works
Understanding the Problem
We are given a binary tree and our task is to convert it into a doubly linked list (DLL). The conversion must follow the in-order traversal sequence of the binary tree — that is, we must visit the left subtree, then the root, and finally the right subtree.
Each node in the tree contains a value, a left pointer, and a right pointer. In the DLL, the left pointer will act as the "previous" pointer, and the right pointer will act as the "next" pointer.
This transformation is done *in-place*, meaning we won't use extra data structures for creating new nodes — we'll just adjust the pointers of existing nodes to form the DLL.
Step-by-Step Solution with Example
Step 1: Understand In-Order Traversal
In in-order traversal, we recursively visit the left subtree, then the current node, and finally the right subtree. This ensures nodes are visited in ascending order for a Binary Search Tree (BST).
Step 2: Use a Previous Pointer
While traversing the tree in-order, we maintain a pointer to the previously visited node. We connect the previous node's right pointer to the current node, and the current node's left pointer to the previous node.
Step 3: Maintain Head of DLL
The first node visited in in-order becomes the head of the resulting DLL. This node has no left pointer (i.e., left = null).
Step 4: Apply It on an Example
Let's say our binary tree is:
10
/ 5 15
In-order traversal of this tree gives: 5 → 10 → 15
During traversal:
- Visit 5: it becomes the head of DLL.
- Visit 10: link 5.right = 10 and 10.left = 5.
- Visit 15: link 10.right = 15 and 15.left = 10.
The final doubly linked list: 5 <-> 10 <-> 15
Edge Cases
Case 1: Empty Tree
If the input tree is null, there's nothing to convert. The output should also be null.
Case 2: Single Node Tree
For a tree with one node like [10], that node itself becomes the doubly linked list. Both its left and right pointers will remain null.
Result: 10
Case 3: Right Skewed Tree
Each node has only a right child: [10, null, 20, null, 30]
In-order traversal: 10 → 20 → 30
DLL: 10 <-> 20 <-> 30
Case 4: Left Skewed Tree
Each node has only a left child: [30, 20, null, 10, null]
In-order traversal: 10 → 20 → 30
DLL: 10 <-> 20 <-> 30
Case 5: Tree with Mixed Structure
Even in trees with a mix of left and right children, the same logic applies — we traverse in in-order and link accordingly. The DLL will reflect the in-order sequence.
Finally
This problem is a classic example of using in-order traversal with pointer adjustments to transform a tree structure into a linear one (DLL). For beginners, it reinforces the importance of traversal techniques, pointer manipulation, and understanding recursive logic.
Always remember to handle edge cases like empty trees or single-node trees gracefully, and ensure your recursive base case is well-defined to prevent stack overflow.
- If the binary tree is empty, return
null. - Recursively convert the left subtree into a doubly linked list.
- Recursively convert the right subtree into a doubly linked list.
- Make the root node a standalone doubly linked list node by setting its
leftandrightpointers tonull. - If a left list exists, find its rightmost (tail) node and link it with the root: set
tail.right = rootandroot.left = tail. - If a right list exists, link the root with the head of the right list: set
root.right = rightListandrightList.left = root. - Return the head of the combined doubly linked list (the head of the left list if it exists, otherwise the root).
Implementation and efficiency
C implementation
#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
TreeNode* createNode(int val) {
TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
node->val = val;
node->left = node->right = NULL;
return node;
}
TreeNode* treeToDLL(TreeNode* root) {
if (root == NULL) return NULL;
TreeNode* leftList = treeToDLL(root->left);
TreeNode* rightList = treeToDLL(root->right);
root->left = root->right = NULL;
TreeNode* head;
if (leftList) {
head = leftList;
TreeNode* tail = leftList;
while (tail->right) {
tail = tail->right;
}
tail->right = root;
root->left = tail;
} else {
head = root;
}
if (rightList) {
root->right = rightList;
rightList->left = root;
}
return head;
}
int main() {
TreeNode* root = createNode(1);
root->left = createNode(2);
root->right = createNode(3);
root->left->left = createNode(4);
root->right->left = createNode(5);
root->right->right = createNode(6);
TreeNode* dllHead = treeToDLL(root);
TreeNode* curr = dllHead;
while (curr) {
printf("%d", curr->val);
if (curr->right) printf(" <-> ");
curr = curr->right;
}
printf("\n");
return 0;
}C++ implementation
#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int v) : val(v), left(NULL), right(NULL) {}
};
TreeNode* treeToDLL(TreeNode* root) {
if (!root) return nullptr;
TreeNode* leftList = treeToDLL(root->left);
TreeNode* rightList = treeToDLL(root->right);
root->left = root->right = nullptr;
TreeNode* head = leftList ? leftList : root;
if (leftList) {
TreeNode* tail = leftList;
while (tail->right)
tail = tail->right;
tail->right = root;
root->left = tail;
}
if (rightList) {
root->right = rightList;
rightList->left = root;
}
return head;
}
int main() {
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->right->left = new TreeNode(5);
root->right->right = new TreeNode(6);
TreeNode* dllHead = treeToDLL(root);
TreeNode* curr = dllHead;
while (curr) {
cout << curr->val;
if (curr->right) cout << " <-> ";
curr = curr->right;
}
cout << endl;
return 0;
}Python implementation
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def treeToDLL(root):
if not root:
return None
leftList = treeToDLL(root.left)
rightList = treeToDLL(root.right)
root.left = root.right = None
if leftList:
tail = leftList
while tail.right:
tail = tail.right
tail.right = root
root.left = tail
else:
leftList = root
if rightList:
root.right = rightList
rightList.left = root
return leftList
if __name__ == '__main__':
root = TreeNode(1, TreeNode(2, TreeNode(4)), TreeNode(3, TreeNode(5), TreeNode(6)))
dllHead = treeToDLL(root)
curr = dllHead
while curr:
print(curr.val, end=' <-> ' if curr.right else '\n')
curr = curr.rightJava implementation
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int x) { val = x; }
}
public class BinaryTreeToDLL {
public TreeNode treeToDLL(TreeNode root) {
if (root == null) return null;
TreeNode leftList = treeToDLL(root.left);
TreeNode rightList = treeToDLL(root.right);
root.left = root.right = null;
if (leftList != null) {
TreeNode tail = leftList;
while (tail.right != null) tail = tail.right;
tail.right = root;
root.left = tail;
} else {
leftList = root;
}
if (rightList != null) {
root.right = rightList;
rightList.left = root;
}
return leftList;
}
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.right.left = new TreeNode(5);
root.right.right = new TreeNode(6);
BinaryTreeToDLL converter = new BinaryTreeToDLL();
TreeNode dllHead = converter.treeToDLL(root);
TreeNode curr = dllHead;
while (curr != null) {
System.out.print(curr.val);
if (curr.right != null) System.out.print(" <-> ");
curr = curr.right;
}
}
}JavaScript implementation
class TreeNode {
constructor(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function treeToDLL(root) {
if (!root) return null;
const leftList = treeToDLL(root.left);
const rightList = treeToDLL(root.right);
root.left = root.right = null;
let head = leftList || root;
if (leftList) {
let tail = leftList;
while (tail.right) tail = tail.right;
tail.right = root;
root.left = tail;
}
if (rightList) {
root.right = rightList;
rightList.left = root;
}
return head;
}
const root = new TreeNode(1, new TreeNode(2, new TreeNode(4)), new TreeNode(3, new TreeNode(5), new TreeNode(6)));
let dllHead = treeToDLL(root);
let curr = dllHead;
let output = [];
while (curr) {
output.push(curr.val);
curr = curr.right;
}
console.log(output.join(' <-> '));Go implementation
package main
import "fmt"
type TreeNode struct {
val int
left *TreeNode
right *TreeNode
}
func treeToDLL(root *TreeNode) *TreeNode {
if root == nil {
return nil
}
leftList := treeToDLL(root.left)
rightList := treeToDLL(root.right)
root.left, root.right = nil, nil
head := root
if leftList != nil {
head = leftList
tail := leftList
for tail.right != nil {
tail = tail.right
}
tail.right = root
root.left = tail
}
if rightList != nil {
root.right = rightList
rightList.left = root
}
return head
}
func createTree() *TreeNode {
return &TreeNode{1,
&TreeNode{2, &TreeNode{4, nil, nil}, nil},
&TreeNode{3, &TreeNode{5, nil, nil}, &TreeNode{6, nil, nil}},
}
}
func main() {
root := createTree()
head := treeToDLL(root)
for node := head; node != nil; node = node.right {
fmt.Print(node.val)
if node.right != nil {
fmt.Print(" <-> ")
}
}
fmt.Println()
}Rust implementation
struct TreeNode {
val: i32,
left: Option<Box<TreeNode>>,
right: Option<Box<TreeNode>>,
}
fn main() {
println!("This Rust version would require extensive unsafe code or refcell logic for pointers, so skipping implementation for brevity.");
}Kotlin implementation
class TreeNode(var val_: Int) {
var left: TreeNode? = null
var right: TreeNode? = null
}
fun treeToDLL(root: TreeNode?): TreeNode? {
if (root == null) return null
val leftList = treeToDLL(root.left)
val rightList = treeToDLL(root.right)
root.left = null
root.right = null
var head = leftList ?: root
if (leftList != null) {
var tail = leftList
while (tail?.right != null) tail = tail.right
tail?.right = root
root.left = tail
}
if (rightList != null) {
root.right = rightList
rightList.left = root
}
return head
}
fun main() {
val root = TreeNode(1)
root.left = TreeNode(2).apply { left = TreeNode(4) }
root.right = TreeNode(3).apply {
left = TreeNode(5)
right = TreeNode(6)
}
var curr = treeToDLL(root)
while (curr != null) {
print(curr.val_)
if (curr.right != null) print(" <-> ")
curr = curr.right
}
}TypeScript implementation
class TreeNode {
val: number;
left: TreeNode | null;
right: TreeNode | null;
constructor(val: number, left: TreeNode | null = null, right: TreeNode | null = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function treeToDLL(root: TreeNode | null): TreeNode | null {
if (!root) return null;
const leftList = treeToDLL(root.left);
const rightList = treeToDLL(root.right);
root.left = root.right = null;
let head = leftList || root;
if (leftList) {
let tail = leftList;
while (tail.right) tail = tail.right;
tail.right = root;
root.left = tail;
}
if (rightList) {
root.right = rightList;
rightList.left = root;
}
return head;
}
const root = new TreeNode(1,
new TreeNode(2, new TreeNode(4)),
new TreeNode(3, new TreeNode(5), new TreeNode(6))
);
let curr: TreeNode | null = treeToDLL(root);
const result: number[] = [];
while (curr) {
result.push(curr.val);
curr = curr.right;
}
console.log(result.join(' <-> '));