Algoritmos e Estruturas de Dados em JavaScript (Node.js): do básico às técnicas avançadas
Compreender algoritmos e estruturas de dados é uma habilidade fundamental para qualquer desenvolvedor que trabalhe com JavaScript, especialmente no ambiente Node.js. Sem esse conhecimento, é impossível criar aplicações eficientes, escaláveis e rápidas. Neste artigo, abordaremos os conceitos-chave que o ajudarão a escrever código otimizado e a ter sucesso em entrevistas técnicas.
Por que algoritmos e estruturas de dados são importantes para Node.js?
Node.js, construído sobre o motor V8, permite executar JavaScript no servidor. Aqui, ao contrário do navegador, o desempenho é crítico: o servidor processa milhares de requisições simultaneamente. A escolha errada de uma estrutura de dados pode tornar o processamento centenas de vezes mais lento. Por exemplo, buscar um elemento em um array de 10.000 registros pode levar 0,1 ms, enquanto em uma lista encadeada pode levar 10 ms. A diferença é evidente.
As principais estruturas de dados que você utilizará:
- Arrays (Array) — uma ferramenta básica, mas poderosa. Acesso rápido por índice (O(1)), mas inserção/remoção no início é lenta (O(n)).
- Tabelas hash (Object, Map) — ideais para armazenar pares "chave-valor". A complexidade média das operações é O(1).
- Listas encadeadas (LinkedList) — eficientes para inserções e remoções frequentes no meio da lista.
- Pilhas e filas (Stack, Queue) — indispensáveis no processamento de fluxos de dados (por exemplo, tarefas no event loop).
- Árvores (Binary Tree, BST) — para dados hierárquicos e busca rápida.
- Grafos (Graph) — para modelar conexões (redes sociais, rotas).
Algoritmos básicos de ordenação e busca em JavaScript
Vamos considerar duas tarefas clássicas: ordenação de array e busca binária. Em Node.js, esses algoritmos são implementados diretamente, sem bibliotecas de terceiros.
Quick Sort (Quick Sort)
Este é um dos algoritmos mais eficientes, com complexidade média O(n log n). Funciona com base no princípio "dividir para conquistar".
function quickSort(arr) { if (arr.length <= 1) return arr;
const pivot = arr[arr.length - 1]; const left = []; const right = [];
for (let i = 0; i < arr.length - 1; i++) { if (arr[i] < pivot) { left.push(arr[i]); } else { right.push(arr[i]); } }
return [...quickSort(left), pivot, ...quickSort(right)];}
// Exemplo de usoconst numbers = [34, 7, 23, 32, 5, 62];console.log(quickSort(numbers)); // [5, 7, 23, 32, 34, 62]Binary Search (Binary Search)
Algoritmo para arrays ordenados. Complexidade O(log n) — uma ordem de grandeza mais rápido que a busca linear.
function binarySearch(arr, target) { let left = 0; let right = arr.length - 1;
while (left <= right) { const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) return mid; if (arr[mid] < target) left = mid + 1; else right = mid - 1; }
return -1; // elemento não encontrado}
const sortedArray = [1, 3, 5, 7, 9, 11, 13];console.log(binarySearch(sortedArray, 7)); // 3Tabelas hash: quando o desempenho é importante
Em Node.js, as tabelas hash são implementadas através dos objetos nativos Map e Object. Elas fornecem acesso aos dados em O(1) em média. No entanto, é importante entender como evitar colisões e perda de desempenho.
Exemplo: contagem de frequência de palavras usando Map
function countWords(text) { const wordMap = new Map(); const words = text.toLowerCase().split(/\\W+/);
for (const word of words) { if (word) { wordMap.set(word, (wordMap.get(word) || 0) + 1); } }
return wordMap;}
const text = "JavaScript e Node.js. JavaScript é uma linguagem de programação";