# Merging two sorted linked lists in kotlin

> code that works is good. code that reads like a story is better.

Published: 2025-04-17

## 💡 Why I Picked This Problem

I've been solving problems on LeetCode lately to sharpen my Kotlin and data structure skills. When I came across "Merge Two Sorted Lists", I thought: Easy. Just compare and connect, right?

Well… not quite.

What looked simple on the surface turned into a mini journey of learning how to:

- Use dummy nodes
- Handle linked list edge cases
- Write idiomatic Kotlin
- And most importantly, go from working code to elegant code

This blog captures that journey.

## 🛠️ The Problem

You're given two sorted singly linked lists. Merge them into one sorted list and return it. The result should reuse the nodes from the original lists — no creating new nodes.

## ❌ My First Attempt (Functional but Flawed)

```kotlin
class Solution {
    fun mergeTwoLists(list1: ListNode?, list2: ListNode?): ListNode? {
        val result = ListNode()
        var l1 = list1
        var l2 = list2
        while(l1?.val != null && l2?.val != null){
            if(l1.val <= l2.val){
                result.next = l1
                l1 = l1.next
            } else {
                result.next = l2
                l2 = l2.next
            }
        }
        result.next = l1?.next ?: l2?.next
        return result.next
    }
}

// ListNode class definition for context
class ListNode(var val: Int = 0) {
    var next: ListNode? = null
}

fun main() {
    // This code won't run correctly due to the flaws discussed below
    println("This is my first flawed attempt - see explanation below")
}
```

At first glance, this feels like it should work… until you test it.

## ❗ What Went Wrong:

1. **I was overwriting result.next on every iteration** — never building a full list.

2. **I forgot to track the tail of the result list.**

3. **I tried to compare val properties safely, but added unnecessary checks.**

4. **My final result.next = l1?.next ?: l2?.next skipped nodes!**

This wasn't just a logic fail — it was a reminder that linked lists don't forgive sloppy thinking.

## 🔁 Refactoring and Debugging

I went back to the drawing board and broke it into smaller pieces:

✅ Use a dummy node to simplify list-building  
✅ Use a tail pointer to track where we're adding next  
✅ Use safe Kotlin null-checking with ?:  
✅ Replace manual pointer updates with Kotlin's also  

That led to something much more elegant.

## ✅ The Final Clean Kotlin Version

```kotlin
class Solution {
    fun mergeTwoLists(list1: ListNode?, list2: ListNode?): ListNode? {
        val dummy = ListNode(-1)
        var tail = dummy
        var l1 = list1
        var l2 = list2

        while (l1 != null && l2 != null) {
            if (l1.\`val\` <= l2.\`val\`) {
                tail.next = l1.also { l1 = it.next }
            } else {
                tail.next = l2.also { l2 = it.next }
            }
            tail = tail.next!!
        }

        tail.next = l1 ?: l2
        return dummy.next
    }
}

// ListNode class definition
class ListNode(var \`val\`: Int = 0) {
    var next: ListNode? = null
}

fun main() {
    // Helper function to create a linked list from an array
    fun createLinkedList(arr: IntArray): ListNode? {
        if (arr.isEmpty()) return null
        val dummy = ListNode(0)
        var current = dummy
        for (value in arr) {
            current.next = ListNode(value)
            current = current.next!!
        }
        return dummy.next
    }

    // Helper function to print a linked list
    fun printLinkedList(head: ListNode?) {
        var current = head
        val result = mutableListOf<Int>()
        while (current != null) {
            result.add(current.\`val\`)
            current = current.next
        }
        println(result.joinToString(" -> "))
    }

    // Test case 1
    val list1 = createLinkedList(intArrayOf(1, 2, 4))
    val list2 = createLinkedList(intArrayOf(1, 3, 4))

    println("List 1:")
    printLinkedList(list1)
    println("List 2:")
    printLinkedList(list2)

    val solution = Solution()
    val merged = solution.mergeTwoLists(list1, list2)

    println("Merged list:")
    printLinkedList(merged)
}
```

## 🧠 What I Love About This Version

✅ Uses Kotlin's also to update references cleanly

✅ Safe handling of null pointers

✅ Dummy node pattern makes it easier to return the result

✅ tail clearly communicates intent — appending to the end of the list

✅ The loop reads naturally — like you're telling Kotlin what to do in plain English

This version passes all test cases and feels solid, simple, and elegant — a big leap from where I started.

## 💬 Key Takeaways

- Don't rush linked list problems — edge cases matter more than you think.

- Kotlin gives you powerful tools to write safe and readable code — use them.

- Start with "does it work" — but always push toward "does it feel clean"

- The also block trick is totally underrated.

## 🔭 What's Next?

I'm considering a follow-up where I:

- Write a recursive version of this function in Kotlin
- Generalize this logic to merge k sorted lists
- Build a Kotlin-style unit test suite to validate edge cases

## 📢 Final Thoughts

There's something deeply satisfying about taking messy, trial-and-error code and shaping it into something that's both correct and beautiful. This little problem — just merging two lists — reminded me that code can be art when you care enough to polish it.

Thanks for reading!
