Introdução: Por que algoritmos e estruturas de dados são importantes em Go
Go (Golang) é uma linguagem compilada moderna, criada no Google para sistemas de alto desempenho. Sua simplicidade e suporte embutido para concorrência a tornam ideal para desenvolvimento backend, microsserviços e aplicações em nuvem. No entanto, como em qualquer outra linguagem, a eficiência do código em Go depende diretamente da compreensão de algoritmos e estruturas de dados. Sem eles, mesmo a linguagem mais rápida pode se tornar um gargalo.
Neste artigo, abordaremos as principais estruturas de dados e algoritmos que todo desenvolvedor Go deve conhecer: de arrays à busca binária. Você verá exemplos de código, aprenderá como eles funcionam "sob o capô" e receberá dicas práticas de otimização.
1. Estruturas de dados básicas em Go
Go fornece estruturas de dados embutidas, mas usá-las corretamente é a chave para o desempenho. Vamos considerar três principais: arrays, slices e hash tables.
1.1 Arrays e Slices
Um array em Go é uma sequência fixa de elementos do mesmo tipo. Um slice é um wrapper dinâmico sobre um array, que permite alterar o tamanho.
package main
import "fmt"
func main() { // Array de tamanho fixo var arr [5]int = [5]int{1, 2, 3, 4, 5} fmt.Println("Array:", arr)
// Slice — estrutura dinâmica slice := []int{10, 20, 30} slice = append(slice, 40) // Adicionando elemento fmt.Println("Slice:", slice)
// Slice a partir de um array subSlice := arr[1:4] // [2, 3, 4] fmt.Println("Sub-slice:", subSlice)}Dica: Sempre use slices em vez de arrays para flexibilidade, mas lembre-se de que append pode causar realocação de memória. Pré-defina a capacidade usando make([]int, 0, capacity).
1.2 Hash Tables (Map)
Map é uma coleção não ordenada de pares chave-valor. Em Go, o map é implementado como uma hash table, garantindo tempo médio de acesso O(1).
package main
import "fmt"
func main() { // Criando um map scores := make(map[string]int) scores["Alice"] = 95 scores["Bob"] = 87
// Leitura com verificação if val, ok := scores["Alice"]; ok { fmt.Println("Nota da Alice:", val) }
// Remoção delete(scores, "Bob") fmt.Println("Após remoção:", scores)}Importante: Map não suporta acesso concorrente sem sincronização. Para cenários multithread, use sync.Map ou mutexes.
2. Implementação de lista encadeada em Go
Listas encadeadas são raras em Go devido à existência de slices, mas são úteis para entender ponteiros e estruturas dinâmicas. Vamos considerar uma lista simplesmente encadeada.
2.1 Estrutura do nó e operações básicas
package main
import "fmt"
// Nó da listatype Node struct { Value int Next *Node}
// Listatype LinkedList struct { Head *Node}
// Adicionar no iníciofunc (ll *LinkedList) Prepend(value int) { newNode := &Node{Value: value, Next: ll.Head} ll.Head = newNode}
// Buscar elementofunc (ll *LinkedList) Search(value int) bool { current := ll.Head for current != nil { if current.Value == value { return true } current = current.Next } return false}
// Exibir listafunc (ll *LinkedList) Print() { current := ll.Head for current != nil { fmt.Printf("%d -> ", current.Value) current = current.Next } fmt.Println("nil")}
func main() { list := LinkedList{} list.Prepend(10) list.Prepend(20) list.Prepend(30) list.Print() // 30 -> 20 -> 10 -> nil fmt.Println("Encontrado 20:", list.Search(20)) // true}Desempenho: Inserção no início — O(1), busca — O(n). Para acesso frequente por índice,