Algorithms and Data Structures in Kotlin: From Theory to Practice
\n\nKotlin is a modern, concise, and safe language actively used for Android development, backend, and even native applications. However, its power is fully revealed only with the proper application of algorithms and data structures. In this article, we will break down key concepts with practical examples that will help you write efficient and maintainable code.
\n\n1. Basic Data Structures: Lists, Stacks, and Queues
\n\nLet's start with the fundamentals. Kotlin provides a rich standard library, but understanding how data structures work "under the hood" is critically important.
\n\nSingly Linked List (LinkedList)
\nLet's implement a simple singly linked list manually. This will help you understand the principles of working with memory and references.
\n\nclass Node<T>(var value: T, var next: Node<T>? = null)\n\nclass LinkedList<T> {\n private var head: Node<T>? = null\n private var tail: Node<T>? = null\n\n fun add(value: T) {\n val node = Node(value)\n if (head == null) {\n head = node\n tail = node\n } else {\n tail?.next = node\n tail = node\n }\n }\n\n fun printAll() {\n var current = head\n while (current != null) {\n print("${current.value} ")\n current = current.next\n }\n println()\n }\n}\n\n// Usage\nfun main() {\n val list = LinkedList<Int>()\n list.add(1)\n list.add(2)\n list.add(3)\n list.printAll() // Output: 1 2 3\n}\n\nArray-Based Stack
\nA stack operates on the LIFO (Last In, First Out) principle. Let's implement it using Kotlin's mutable list.
\n\nclass Stack<T> {\n private val elements = mutableListOf<T>()\n\n fun push(item: T) = elements.add(item)\n fun pop(): T? = if (elements.isNotEmpty()) elements.removeAt(elements.size - 1) else null\n fun peek(): T? = elements.lastOrNull()\n fun isEmpty() = elements.isEmpty()\n}\n\n// Example: Bracket validation\nfun isBalanced(expression: String): Boolean {\n val stack = Stack<Char>()\n for (char in expression) {\n when (char) {\n '(', '{', '[' -> stack.push(char)\n ')' -> if (stack.pop() != '(') return false\n '}' -> if (stack.pop() != '{') return false\n ']' -> if (stack.pop() != '[') return false\n }\n }\n return stack.isEmpty()\n}\n\nfun main() {\n println(isBalanced("({[]})")) // true\n println(isBalanced("({[})")) // false\n}\n\n2. Sorting Algorithms: From Bubble to Quick Sort
\n\nSorting is one of the most studied topics. Let's look at three classic algorithms that are easy to implement in Kotlin.
\n\nBubble Sort
\nSimple but inefficient for large arrays (O(n²)).
\n\nfun bubbleSort(arr: IntArray) {\n val n = arr.size\n for (i in 0 until n - 1) {\n for (j in 0 until n - i - 1) {\n if (arr[j] > arr[j + 1]) {\n val temp = arr[j]\n arr[j] = arr[j + 1]\n arr[j + 1] = temp\n }\n }\n }\n}\n\nfun main() {\n val array = intArrayOf(64, 34, 25, 12, 22, 11, 90)\n bubbleSort(array)\n println(array.joinToString()) // 11, 12, 22, 25, 34, 64, 90\n}\n\nQuick Sort
\nOne of the fastest algorithms on average (O(n log n)). It uses recursion and array partitioning.
\n\nfun quickSort(arr: IntArray, low: Int, high: Int) {\n if (low < high) {\n val pi = partition(arr, low, high)\n quickSort(arr, low, pi - 1)\n quickSort(arr, pi + 1, high)\n }\n}\n\nfun partition(arr: IntArray, low: Int, high: Int): Int {\n val pivot = arr[high]\n var i = low - 1\n for (j in low until high) {\n