Algoritmos y estructuras de datos en Kotlin: en la práctica
Kotlin es un lenguaje multiparadigma moderno que se utiliza activamente para el desarrollo de Android, backend e incluso aplicaciones de escritorio. Sin embargo, para escribir código eficiente y escalable, no basta con conocer la sintaxis: es necesario comprender cómo funcionan los algoritmos y las estructuras de datos. En este artículo, analizaremos los conceptos clave en la práctica con ejemplos en Kotlin. Aprenderás cómo implementar estructuras y algoritmos básicos, así como cómo utilizar las capacidades integradas del lenguaje para optimizar el código.
1. Estructuras de datos básicas: arreglos y listas
En Kotlin, los arreglos se representan con el tipo Array<T>, y las listas dinámicas con List<T> (inmutable) y MutableList<T> (mutable). Sin embargo, en problemas reales a menudo es necesario implementar una lista enlazada propia para comprender los principios de funcionamiento.
Lista simplemente enlazada en Kotlin
Una lista enlazada consta de nodos, cada uno de los cuales contiene datos y una referencia al siguiente nodo. Veamos una implementación básica:
class Node<T>(var value: T, var next: Node<T>? = null)
class LinkedList<T> { private var head: Node<T>? = null
fun add(value: T) { val newNode = Node(value) if (head == null) { head = newNode } else { var current = head while (current?.next != null) { current = current.next } current?.next = newNode } }
fun printAll() { var current = head while (current != null) { print("${current.value} ") current = current.next } println() }}
fun main() { val list = LinkedList<Int>() list.add(1) list.add(2) list.add(3) list.printAll() // Salida: 1 2 3}Esta implementación demuestra las operaciones básicas: agregar al final y recorrer. En proyectos reales, usa las colecciones integradas de Kotlin, pero comprender el funcionamiento interno ayuda a evitar errores al trabajar con memoria y rendimiento.
2. Algoritmos de ordenamiento en Kotlin
El ordenamiento es una de las operaciones más comunes. En Kotlin existe la función integrada sorted(), pero para una comprensión profunda es útil implementar algoritmos clásicos.
Ordenamiento rápido (QuickSort)
QuickSort es uno de los algoritmos más eficientes con una complejidad promedio de O(n log n). Implementación en Kotlin usando recursividad:
fun quickSort(arr: IntArray, low: Int, high: Int) { if (low < high) { val pivotIndex = partition(arr, low, high) quickSort(arr, low, pivotIndex - 1) quickSort(arr, pivotIndex + 1, high) }}
fun partition(arr: IntArray, low: Int, high: Int): Int { val pivot = arr[high] var i = low - 1 for (j in low until high) { if (arr[j] <= pivot) { i++ arr[i] = arr[j].also { arr[j] = arr[i] } } } arr[i + 1] = arr[high].also { arr[high] = arr[i + 1] } return i + 1}
fun main() { val array = intArrayOf(10, 7, 8, 9, 1, 5) quickSort(array, 0, array.size - 1) println(array.joinToString()) // Salida: 1, 5, 7, 8, 9, 10}Observa el uso de also para intercambiar valores: esta es una forma idiomática en Kotlin. El algoritmo es estable y funciona in-place, lo que ahorra memoria.
3. Búsqueda: binaria y lineal
La búsqueda de elementos en estructuras de datos es la segunda tarea más importante. Consideremos dos enfoques.
Búsqueda lineal
Un algoritmo simple pero lento (O(n)). Adecuado para datos no ordenados:
fun linearSearch(arr: IntArray, target: Int): Int { for (i in arr.indices) { if (arr[i] == target) return i } retu