Algoritmos e Estruturas de Dados em Go: do Básico à Prática
Go (Golang) é uma linguagem compilada com tipagem forte, criada no Google para resolver problemas de alta performance e escalabilidade. Ao contrário do Python, que é frequentemente usado para prototipagem, Go exige do desenvolvedor um profundo entendimento do gerenciamento de memória e eficiência algorítmica. Neste artigo, vamos analisar as principais estruturas de dados e algoritmos em Go com exemplos práticos de código.
Por que é importante estudar algoritmos e estruturas de dados em Go?
Go é amplamente usado em desenvolvimento backend, microsserviços, ferramentas DevOps (Docker, Kubernetes) e sistemas de alta carga. Sem entender estruturas de dados, você não conseguirá:
- Otimizar o tempo de execução de consultas a bancos de dados
- Escrever handlers eficientes para requisições HTTP
- Trabalhar com grandes volumes de dados em tempo real
- Passar em entrevistas técnicas nas principais empresas de TI
Go fornece estruturas embutidas: array, slice, map, mas para problemas complexos (como grafos ou árvores) é necessário implementá-las manualmente. Vamos analisar cada estrutura.
1. Estruturas de Dados Básicas em Go
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 (array dinâmico) slice := []int{10, 20, 30} slice = append(slice, 40, 50) fmt.Println("Slice:", slice)
// Fatiamento de slice subSlice := slice[1:4] fmt.Println("Sub-slice:", subSlice)}Quando usar: Arrays — para dados fixos (ex.: coordenadas de um ponto). Slices — para qualquer coleção dinâmica. Slices em Go são análogos às listas em Python, mas com gerenciamento de memória mais eficiente.
Tabelas Hash (Map)
Map é uma tabela hash embutida que fornece acesso aos dados em O(1) no caso médio.
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("Alice:", val) }
// Remoção delete(scores, "Bob")
// Iteração for name, score := range scores { fmt.Printf("%s: %d", name, score) }}Dica: Em Go, o map não garante a ordem dos elementos. Se precisar de um dicionário ordenado, use um slice de pares chave-valor com ordenação.
2. Implementação de Estruturas de Dados Complexas
Pilha e Fila
Em Go não há pilha ou fila embutidas, mas é fácil implementá-las usando slices.
package main
import "fmt"
// Pilha (LIFO)type Stack []int
func (s *Stack) Push(v int) { *s = append(*s, v)}
func (s *Stack) Pop() (int, bool) { if len(*s) == 0 { return 0, false } v := (*s)[len(*s)-1] *s = (*s)[:len(*s)-1] return v, true}
// Fila (FIFO)type Queue []int
func (q *Queue) Enqueue(v int) { *q = append(*q, v)}
func (q *Queue) Dequeue() (int, bool) { if len(*q) == 0 { return 0, false } v := (*q)[0] *q = (*q)[1:] return v, true}
func main() { stack := Stack{} stack.Push(1) stack.Push(2) fmt.Println("Pop:", stack.Pop()) // 2
queue := Queue{} queue.Enqueue(10) queue.Enqueue(20) fmt.Println("Dequeue:", queue.Dequeue()) // 10}Performance: Inserção e remoção no início de um slice (fila) — O(n