Algoritmos e Estruturas de Dados em C++: da Teoria à Prática
\n\nDominar algoritmos e estruturas de dados é uma etapa fundamental para se tornar um desenvolvedor profissional. Sem um entendimento profundo de como organizar e processar dados de forma eficiente, é impossível criar aplicações performáticas e escaláveis. O C++ oferece capacidades únicas para trabalho de baixo nível com memória e alta performance, tornando-o a linguagem ideal para estudar este tema.
\n\nNeste artigo, abordaremos algoritmos e estruturas de dados fundamentais em C++ com exemplos práticos de código que você poderá aplicar imediatamente em seus projetos. Não nos aprofundaremos em cálculos matemáticos abstratos — nosso foco está na implementação real e na compreensão dos princípios de funcionamento.
\n\n1. Arrays Dinâmicos e Gerenciamento de Memória
\n\nVamos começar pelo básico — arrays dinâmicos. Diferente dos arrays estáticos, cujo tamanho é fixo em tempo de compilação, os arrays dinâmicos permitem alterar o tamanho durante a execução do programa. Em C++, usamos os operadores new[] e delete[] para isso.
#include <iostream>\n\nint main() {\n int size = 5;\n int* arr = new int[size]; // alocação de memória\n \n // Inicialização\n for(int i = 0; i < size; i++) {\n arr[i] = i * 10;\n }\n \n // Exibição\n for(int i = 0; i < size; i++) {\n std::cout << arr[i] << " ";\n }\n \n delete[] arr; // liberação de memória\n return 0;\n}\n\nÉ importante lembrar que, ao trabalhar com memória dinâmica, você é responsável por liberá-la. Vazamentos de memória são um dos problemas mais comuns em C++. Recomenda-se o uso de ponteiros inteligentes (std::unique_ptr, std::shared_ptr) da biblioteca padrão para gerenciamento automático de memória.
2. Pilha e Fila: Estruturas de Dados Clássicas
\n\nPilha e fila são duas estruturas de dados fundamentais que implementam os princípios LIFO (Last In, First Out) e FIFO (First In, First Out), respectivamente. Vamos ver suas implementações em C++ usando arrays dinâmicos.
\n\nImplementação da Pilha
\n\n#include <iostream>\n#include <stdexcept>\n\nclass Stack {\nprivate:\n int* data;\n int capacity;\n int top;\n \npublic:\n Stack(int size) : capacity(size), top(-1) {\n data = new int[capacity];\n }\n \n ~Stack() {\n delete[] data;\n }\n \n void push(int value) {\n if(top >= capacity - 1) {\n throw std::overflow_error("Stack overflow");\n }\n data[++top] = value;\n }\n \n int pop() {\n if(isEmpty()) {\n throw std::underflow_error("Stack underflow");\n }\n return data[top--];\n }\n \n bool isEmpty() const {\n return top == -1;\n }\n \n int peek() const {\n if(isEmpty()) {\n throw std::underflow_error("Stack is empty");\n }\n return data[top];\n }\n};\n\nint main() {\n Stack stack(5);\n stack.push(10);\n stack.push(20);\n stack.push(30);\n \n std::cout << "Elemento do topo: " << stack.peek() << std::endl;\n std::cout << "Removendo: " << stack.pop() << std::endl;\n std::cout << "Removendo: " << stack.pop() << std::endl;\n \n return 0;\n}\n\nImplementação da Fila
\n\n#include <iostream>\n\nclass Queue {\nprivate:\n int* data;\n int capacity;\n int front;\n int rear;\n int count;\n \npublic:\n Queue(int size) : capacity(size), front(0), rear(-1), count(0) {\n data = new int[capacity];\n }\n \n ~Queue() {\n delete[] data;\n }\n \n void enqueue(int value) {\n if(isFull()) {\n throw std::overflow_error("Queue overflow");\n }\n rear = (rear + 1) %