Algoritmos e estruturas de dados em C#: fundamento para código .NET eficiente
No desenvolvimento moderno na plataforma .NET, o conhecimento de algoritmos e estruturas de dados é uma habilidade obrigatória para criar aplicações performáticas e escaláveis. Independentemente de você estar escrevendo web services em ASP.NET Core, aplicações desktop em WPF ou soluções mobile em Xamarin, a escolha correta da estrutura de dados impacta diretamente a velocidade de execução do programa e o consumo de memória. Neste artigo, vamos analisar as principais estruturas de dados e algoritmos na linguagem C#, acompanhando cada seção com exemplos práticos de código.
1. Estruturas de dados básicas em C#: arrays e listas
Vamos começar pelo mais fundamental. Em C#, existem vários tipos embutidos para armazenar coleções, e entender seu funcionamento interno é criticamente importante.
Arrays (Array)
O array é uma sequência fixa de elementos do mesmo tipo. Ele oferece o acesso mais rápido por índice (O(1)), mas não permite alterar o tamanho após a criação.
int[] numbers = new int[5] { 10, 20, 30, 40, 50 };// Acesso por índiceint thirdElement = numbers[2]; // 30// Alteração de elementonumbers[3] = 100;Use arrays quando a quantidade de elementos for conhecida antecipadamente e não for alterada (por exemplo, para armazenar constantes ou buffers fixos).
Lista (List<T>)
List<T> é um array dinâmico. Ele se expande automaticamente ao adicionar novos elementos. Internamente, ele usa um array comum que é recriado quando a capacidade é excedida.
List<string> fruits = new List<string>();fruits.Add("Maçã");fruits.Add("Banana");fruits.Add("Laranja");Console.WriteLine(fruits.Count); // 3fruits.RemoveAt(1); // Remove "Banana"List<T> é a escolha universal para a maioria das tarefas que exigem acesso sequencial e alterações frequentes no tamanho da coleção.
2. Pilha e fila: princípios LIFO e FIFO
Essas duas estruturas de dados implementam regras rigorosas para adicionar e remover elementos. Elas são indispensáveis na análise de expressões, processamento de tarefas atrasadas e implementação de algoritmos de travessia.
Pilha (Stack<T>)
A pilha funciona pelo princípio "último a entrar, primeiro a sair" (LIFO). No .NET, a pilha é implementada como uma coleção genérica.
Stack<int> stack = new Stack<int>();stack.Push(1);stack.Push(2);stack.Push(3);
int top = stack.Pop(); // 3int peek = stack.Peek(); // 2 (sem remover)Exemplo real: verificar a correção de parênteses em uma string ou implementar o botão "Voltar" no navegador.
Fila (Queue<T>)
A fila segue o princípio "primeiro a entrar, primeiro a sair" (FIFO). É a escolha ideal para processar tarefas na ordem em que chegam.
Queue<string> queue = new Queue<string>();queue.Enqueue("Tarefa 1");queue.Enqueue("Tarefa 2");queue.Enqueue("Tarefa 3");
string task = queue.Dequeue(); // "Tarefa 1"string nextTask = queue.Peek(); // "Tarefa 2"Filas são amplamente utilizadas em padrões Producer-Consumer, no processamento de mensagens e em sistemas de logging.
3. Árvores e algoritmos recursivos em C#
Árvores são estruturas hierárquicas onde cada nó pode ter vários nós filhos. Um lugar especial é ocupado pelas árvores binárias de busca (BST), que garantem busca, inserção e remoção rápidas (O(log n) em média).
Implementação de uma árvore binária de busca
public class TreeNode{ public int Value; public TreeNode Left; public TreeNode Right;
public TreeNode(int value) { Value = value; }}
public class BinarySearchTree{ public TreeNode Root;
public void Insert(int value) { Root = InsertRec(Root, value); }
private TreeNode InsertRec(TreeNode root, int value) { if (root == nu