Post

Binary Tree : DFS and BFS Explained

Binary Tree Traversals in Kotlin: DFS and BFS Explained

Binary Tree : DFS and BFS Explained

Binary Trees are the foundation for many advanced data structures like AVL trees, Red-Black trees, and Heaps. To work with them effectively, you must master the two primary ways to visit every node: Depth-First Search (DFS) and Breadth-First Search (BFS).

The Foundation: BinaryTreeNode

In Kotlin, we represent a tree node using a data class with nullable references for the left and right children.

1
2
3
4
5
data class BinaryTreeNode(
    val data: Int, 
    var left: BinaryTreeNode? = null, 
    var right: BinaryTreeNode? = null
)

1. Depth-First Search (DFS)

DFS uses recursion to go as deep as possible down one branch before backing up. There are three classic DFS orders:

Pre-Order (Root -> Left -> Right)

Perfect for creating a copy of a tree.

1
2
3
4
5
6
fun dfsPrintPreOrder(node: BinaryTreeNode?) {
    if (node == null) return
    print("${node.data} ")
    dfsPrintPreOrder(node.left)
    dfsPrintPreOrder(node.right)
}

2. In-Order (Left -> Root -> Right)

If used on a Binary Search Tree (BST), this returns the values in sorted order.

1
2
3
4
5
6
fun dfsPrintInOrder(node: BinaryTreeNode?) {
    if (node == null) return
    dfsPrintInOrder(node.left)
    print("${node.data} ")
    dfsPrintInOrder(node.right)
}

Post-Order (Left -> Right -> Root)

Used for deleting trees or calculating the size of a directory.

1
2
3
4
5
6
fun dfsPrintPostOrder(node: BinaryTreeNode?) {
    if (node == null) return
    dfsPrintPostOrder(node.left)
    dfsPrintPostOrder(node.right)
    print("${node.data} ")
}

2. Breadth-First Search (BFS)

BFS, also known as Level-Order Traversal, visits all nodes at the current depth before moving to the next level. We use a Queue to keep track of nodes to visit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fun bfsReturnOrder(root: BinaryTreeNode): List<List<Int>> {
    val queue = ArrayDeque<BinaryTreeNode>()
    val outputList = mutableListOf<List<Int>>()
    queue.add(root)

    while (queue.isNotEmpty()) {
        val currQueueLength = queue.size
        val levelList = mutableListOf<Int>()
        
        for (i in 0 until currQueueLength) {
            val currNode = queue.removeFirst()
            levelList.add(currNode.data)

            if (currNode.left != null) queue.add(currNode.left!!)
            if (currNode.right != null) queue.add(currNode.right!!)
        }
        outputList.add(levelList)
    }
    return outputList
}

DFS vs BFS: Which one to use?

FeatureDFS (Recursive)BFS (Iterative)
Data StructureStack (Call Stack)Queue
MemoryO(Height)O(Width)
Best ForFinding paths, Game TreesShortest path, Level grouping

Summary

DFS is usually easier to implement via recursion, but BFS is essential when you need to process a tree level-by-level. Understanding the timing of when you “visit” the root (Pre, In, or Post) is the key to solving most tree-based interview questions.

Source Code

🚀 Code: All the code for this tutorial is available in my DSA-Kotlin Repository.

This post is licensed under CC BY 4.0 by the author.