Kotlin Program to Implement Tim Sort

Kotlin Program to Implement Tim Sort

Sorting is one of the most common tasks in programming. From arranging student scores to organizing product prices, sorting algorithms help us make sense of data efficiently. Among the various sorting algorithms available, Tim Sort stands out because it combines the strengths of Merge Sort and Insertion Sort, making it very efficient for real-world data that often has partially ordered sequences.

Pluralsight Logo
Accelerate your tech career
with hands-on learning.
Whether you're a tech newbie or a total pro,
get the skills and confidence to land your next move.
Start 10-Day Free Trial

Tim Sort is designed to take advantage of existing order in data. It divides the array into small segments called “runs,” sorts these runs using insertion sort, and then merges them using a merge procedure similar to Merge Sort. This approach reduces unnecessary comparisons and is particularly fast for data that already contains some sorted sequences. Tim Sort is widely used in programming languages like Java and Python, and learning it in Kotlin can give beginners an edge in understanding advanced sorting techniques.

Program 1: Basic Tim Sort Implementation

This program demonstrates the fundamental approach to Tim Sort using predefined runs. It sorts small runs using insertion sort and then merges them to create a fully sorted array.

fun insertionSort(arr: IntArray, left: Int, right: Int) {

    for (i in left + 1..right) {

        val key = arr[i]
        var j = i - 1

        while (j >= left && arr[j] > key) {
            arr[j + 1] = arr[j]
            j--
        }

        arr[j + 1] = key

    }

}

fun merge(arr: IntArray, l: Int, m: Int, r: Int) {

    val len1 = m - l + 1
    val len2 = r - m
    val left = IntArray(len1) { arr[l + it] }
    val right = IntArray(len2) { arr[m + 1 + it] }

    var i = 0
    var j = 0
    var k = l

    while (i < len1 && j < len2) {

        if (left[i] <= right[j]) {
            arr[k] = left[i]
            i++
        } else {
            arr[k] = right[j]
            j++
        }

        k++

    }

    while (i < len1) {
        arr[k++] = left[i++]
    }

    while (j < len2) {
        arr[k++] = right[j++]
    }

}

fun timSort(arr: IntArray) {

    val RUN = 32
    var n = arr.size

    var i = 0

    while (i < n) {

        insertionSort(arr, i, Math.min(i + RUN - 1, n - 1))
        i += RUN

    }

    var size = RUN

    while (size < n) {

        var left = 0

        while (left < n) {

            val mid = left + size - 1
            val right = Math.min(left + 2 * size - 1, n - 1)

            if (mid < right) merge(arr, left, mid, right)

            left += 2 * size

        }

        size *= 2

    }

}

fun main() {

    val numbers = intArrayOf(5, 21, 7, 23, 19, 4, 8, 12, 16, 3)

    println("Original Array: ${numbers.joinToString()}")

    timSort(numbers)

    println("Sorted Array: ${numbers.joinToString()}")

}

This program first divides the array into small segments of size 32 (or smaller if the remaining elements are fewer). Each segment is sorted using insertion sort, and then the merge procedure combines them. Beginners can see how combining two simpler sorting techniques can result in a faster, practical algorithm.

Program 2: Tim Sort for Descending Order

Tim Sort can be easily adapted to sort in descending order by changing the comparison operators in insertion sort and merge functions.

fun insertionSortDesc(arr: IntArray, left: Int, right: Int) {

    for (i in left + 1..right) {

        val key = arr[i]
        var j = i - 1

        while (j >= left && arr[j] < key) {
            arr[j + 1] = arr[j]
            j--
        }

        arr[j + 1] = key

    }

}

fun mergeDesc(arr: IntArray, l: Int, m: Int, r: Int) {

    val len1 = m - l + 1
    val len2 = r - m
    val left = IntArray(len1) { arr[l + it] }
    val right = IntArray(len2) { arr[m + 1 + it] }

    var i = 0
    var j = 0
    var k = l

    while (i < len1 && j < len2) {

        if (left[i] >= right[j]) {
            arr[k] = left[i]
            i++
        } else {
            arr[k] = right[j]
            j++
        }

        k++

    }

    while (i < len1) arr[k++] = left[i++]
    while (j < len2) arr[k++] = right[j++]

}

fun timSortDesc(arr: IntArray) {

    val RUN = 32
    var n = arr.size

    var i = 0

    while (i < n) {

        insertionSortDesc(arr, i, Math.min(i + RUN - 1, n - 1))

        i += RUN

    }

    var size = RUN

    while (size < n) {

        var left = 0

        while (left < n) {

            val mid = left + size - 1
            val right = Math.min(left + 2 * size - 1, n - 1)

            if (mid < right) mergeDesc(arr, left, mid, right)

            left += 2 * size

        }

        size *= 2

    }

}

fun main() {

    val numbers = intArrayOf(5, 21, 7, 23, 19, 4, 8, 12, 16, 3)

    println("Original Array: ${numbers.joinToString()}")

    timSortDesc(numbers)

    println("Sorted in Descending Order: ${numbers.joinToString()}")

}

By reversing the comparison operators in both insertion sort and merge steps, the algorithm now sorts from largest to smallest. This demonstrates how versatile Tim Sort is for different sorting needs.

Program 3: Tim Sort Using Kotlin Lists

Kotlin lists can be used instead of arrays. This version demonstrates Tim Sort on a mutable list, highlighting the flexibility of the algorithm in Kotlin.

fun insertionSort(arr: IntArray, left: Int, right: Int) {

    for (i in left + 1..right) {

        val key = arr[i]
        var j = i - 1

        while (j >= left && arr[j] > key) {
            arr[j + 1] = arr[j]
            j--
        }

        arr[j + 1] = key

    }

}

fun merge(arr: IntArray, l: Int, m: Int, r: Int) {

    val len1 = m - l + 1
    val len2 = r - m
    val left = IntArray(len1) { arr[l + it] }
    val right = IntArray(len2) { arr[m + 1 + it] }

    var i = 0
    var j = 0
    var k = l

    while (i < len1 && j < len2) {

        if (left[i] <= right[j]) {
            arr[k] = left[i]
            i++
        } else {
            arr[k] = right[j]
            j++
        }

        k++

    }

    while (i < len1) {
        arr[k++] = left[i++]
    }

    while (j < len2) {
        arr[k++] = right[j++]
    }

}

fun timSort(arr: IntArray) {

    val RUN = 32
    var n = arr.size

    var i = 0

    while (i < n) {

        insertionSort(arr, i, Math.min(i + RUN - 1, n - 1))
        i += RUN

    }

    var size = RUN

    while (size < n) {

        var left = 0

        while (left < n) {

            val mid = left + size - 1
            val right = Math.min(left + 2 * size - 1, n - 1)

            if (mid < right) merge(arr, left, mid, right)

            left += 2 * size

        }

        size *= 2

    }

}

fun timSortList(list: MutableList<Int>) {

    val arr = list.toIntArray()

    timSort(arr)

    list.clear()
    list.addAll(arr.toList())

}

fun main() {

    val numbers = mutableListOf(12, 5, 8, 19, 3, 15, 7)

    println("Original List: $numbers")

    timSortList(numbers)

    println("Sorted List: $numbers")

}

This approach converts the list to an array, sorts it, and updates the list. Beginners can see how Tim Sort can work seamlessly with different Kotlin data structures while keeping the core algorithm intact.

Program 4: Tim Sort with Custom Run Size

Tim Sort’s performance can be tuned by changing the size of small runs. This program allows setting a custom run size to optimize sorting based on data characteristics.

fun insertionSort(arr: IntArray, left: Int, right: Int) {

    for (i in left + 1..right) {

        val key = arr[i]
        var j = i - 1

        while (j >= left && arr[j] > key) {
            arr[j + 1] = arr[j]
            j--
        }

        arr[j + 1] = key

    }

}

fun merge(arr: IntArray, l: Int, m: Int, r: Int) {

    val len1 = m - l + 1
    val len2 = r - m
    val left = IntArray(len1) { arr[l + it] }
    val right = IntArray(len2) { arr[m + 1 + it] }

    var i = 0
    var j = 0
    var k = l

    while (i < len1 && j < len2) {

        if (left[i] <= right[j]) {
            arr[k] = left[i]
            i++
        } else {
            arr[k] = right[j]
            j++
        }

        k++

    }

    while (i < len1) {
        arr[k++] = left[i++]
    }

    while (j < len2) {
        arr[k++] = right[j++]
    }

}

fun timSortCustomRun(arr: IntArray, RUN: Int) {

    var n = arr.size

    var i = 0

    while (i < n) {

        insertionSort(arr, i, Math.min(i + RUN - 1, n - 1))
        i += RUN

    }

    var size = RUN

    while (size < n) {

        var left = 0

        while (left < n) {

            val mid = left + size - 1
            val right = Math.min(left + 2 * size - 1, n - 1)

            if (mid < right) merge(arr, left, mid, right)

            left += 2 * size

        }

        size *= 2

    }

}

fun main() {

    val numbers = intArrayOf(32, 10, 50, 23, 7, 41, 15)

    println("Original Array: ${numbers.joinToString()}")

    timSortCustomRun(numbers, 16)

    println("Sorted Array with Custom Run Size: ${numbers.joinToString()}")

}

Customizing the run size helps learners understand how Tim Sort can adapt to different datasets, giving better performance for partially sorted arrays.

Frequently Asked Questions (FAQ)

Here are common questions about Tim Sort in Kotlin.

Q1: What is Tim Sort?
Tim Sort is a hybrid sorting algorithm combining insertion sort and merge sort. It is optimized for partially sorted data.

Q2: Why is Tim Sort fast?
It takes advantage of existing runs in data and minimizes unnecessary comparisons and swaps.

Q3: Can Tim Sort sort in descending order?
Yes, by changing comparison operators in the insertion and merge steps, it can sort from largest to smallest.

Q4: What is the time complexity of Tim Sort?
Tim Sort has a worst-case complexity of O(n log n) and is highly efficient for real-world data with existing order.

Q5: Can Tim Sort work with Kotlin lists?
Yes, you can convert lists to arrays for sorting and then update the list, keeping the algorithm flexible.

Conclusion

Tim Sort is a powerful and practical sorting algorithm that blends the simplicity of insertion sort with the efficiency of merge sort. In Kotlin, it can be implemented on arrays, lists, and customized with different run sizes for optimal performance. For beginners, studying Tim Sort helps understand hybrid sorting techniques and prepares them for real-world scenarios where data often contains partially sorted sequences. By practicing Tim Sort, learners can improve both their coding skills and understanding of efficient algorithms.

Scroll to Top