Algoritmos e estruturas de dados em Kotlin: na prática
Kotlin é uma linguagem moderna e multiparadigma, amplamente utilizada para desenvolvimento Android, backend e até aplicações desktop. No entanto, para escrever código eficiente e escalável, não basta conhecer a sintaxe — é necessário entender como funcionam os algoritmos e estruturas de dados. Neste artigo, abordaremos os conceitos-chave na prática com exemplos em Kotlin. Você aprenderá como implementar estruturas e algoritmos básicos, além de como usar os recursos nativos da linguagem para otimizar o código.
1. Estruturas de dados básicas: arrays e listas
Em Kotlin, arrays são representados pelo tipo Array<T>, e listas dinâmicas por List<T> (imutável) e MutableList<T> (mutável). No entanto, em problemas reais, muitas vezes é necessário implementar sua própria lista encadeada para entender os princípios de funcionamento.
Lista encadeada simples em Kotlin
Uma lista encadeada consiste em nós, cada um contendo dados e uma referência ao próximo nó. Vejamos uma implementação 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() // Saída: 1 2 3}Esta implementação demonstra operações básicas: adicionar ao final e percorrer. Em projetos reais, use as coleções nativas do Kotlin, mas entender o funcionamento interno ajuda a evitar erros relacionados a memória e desempenho.
2. Algoritmos de ordenação em Kotlin
Ordenação é uma das operações mais comuns. Em Kotlin, existe a função nativa sorted(), mas para um entendimento profundo, é útil implementar algoritmos clássicos.
QuickSort (Ordenação Rápida)
QuickSort é um dos algoritmos mais eficientes, com complexidade média O(n log n). Implementação em Kotlin usando recursão:
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()) // Saída: 1, 5, 7, 8, 9, 10}Observe o uso de also para trocar valores — esta é uma forma idiomática em Kotlin. O algoritmo é estável e opera in-place, economizando memória.
3. Busca: binária e linear
A busca de elementos em estruturas de dados é a segunda tarefa mais importante. Vejamos duas abordagens.
Busca linear
Algoritmo simples, porém lento (O(n)). Adequado para dados não ordenados:
fun linearSearch(arr: IntArray, target: Int): Int { for (i in arr.indices) { if (arr[i] == target) return i } retu