- Publicado em
Um Resumo dos Mais Utilizados
- Um Resumo dos Mais Utilizados
- Implementando Arrays com NodeJS e Javascript
- Implementando Listas Ligadas (Linked Lists) com NodeJS e Javascript
- Implementando Pilhas (Stacks) com NodeJS e Javascript
- Implementando Filas (Queues) com NodeJS e Javascript
- Implementando Árvores (Trees) com NodeJS e Javascript
- Implementando Grafos (Graphs) com NodeJS e Javascript
- Implementando Tabelas Hash (Hash Tables) com NodeJS e Javascript
- Implementando Heaps com NodeJS e Javascript
- Implementando Filas de Prioridade (Priority Queues) com NodeJS e Javascript
- Implementando Mapas e Conjuntos (Maps/Sets) com NodeJS e Javascript
- Implementando Árvore de Busca Binária com NodeJS e Javascript
- Implementando Tries (Árvores) com NodeJS e Javascript
- Algoritmos de Grafos com NodeJS e Javascript
- Algoritmos de Ordenação com NodeJS e Javascript
- Implementando Busca Binária em Arrays com NodeJS e Javascript
O Poder Oculto das Estruturas de Dados e Algoritmos (DSA)
No dia a dia dos projetos de TI, quando estamos programando, estamos sempre focados em entregar nossos produtos e recursos no prazo certo. Às vezes, esquecemos da importância dos algoritmos e estruturas de dados para fazer nosso código funcionar de forma mais eficiente.
Mas sabia que esses conceitos são como os pilares que sustentam a eficiência do nosso trabalho? Vamos dar uma olhada juntos em alguns dos principais algoritmos e estruturas de dados, entender como eles funcionam e onde podemos usá-los.
As 15 Estruturas de Dados e Algoritmos Essenciais na Programação
O que eu mais quero é te mostrar algumas coisas legais: estruturas, algoritmos, e como eles estão presentes no nosso dia a dia, a minha ideia aqui é usar exemplos bem simples, nada complicado (vou tentar prometo).
Assim, a gente pode lembrar e aproveitar esses recursos poderosos sem stress. Vambora!
1. Arrays (Lists): A Simplicidade na Organização de Dados
Arrays são como listas que guardam elementos em sequência, facilitando guardar e acessar dados rapidamente por meio de índices. São super comuns na programação para várias necessidades, como listas de tarefas, compras ou produtos.
Basicamente, sempre que você tem uma coleção de itens organizados, provavelmente está usando um array. Pegou a ideia né?
2. Listas Ligadas (Linked Lists): Flexibilidade em Armazenamento
Listas ligadas são como um trem de dados onde cada vagão, ou “nó”, não só carrega uma informação, mas também indica qual é o próximo vagão na linha.
Diferente dos arrays, cada elemento é ligado ao próximo, facilitando adicionar ou remover itens sem mexer no resto da “linha”. Nas listas duplamente ligadas, cada nó conhece tanto o seu próximo quanto o anterior, tornando ainda mais fácil navegar para frente e para trás. Isso é ótimo para tarefas como inserções e exclusões rápidas.
Um exemplo prático é o histórico do navegador, onde cada página visitada é um nó, permitindo ir e voltar facilmente entre as páginas. Lista ligada:
Lista duplamente ligada:
3. Pilhas (Stacks): LIFO na Prática
Stacks, ou pilhas, seguem a regra de “último a entrar, primeiro a sair” (LIFO [last in, first out]) aqui faz sentido a famosa frase: "os últimos serão os primeiros"). Pilhas são ideais para controlar sequências de ações, como históricos, onde o mais recente é sempre o primeiro a ser acessado ou algo como algoritmos de reversão.
Imagine uma pilha de pratos: o último prato colocado é o primeiro a ser retirado. Um histórico de alterações segue o mesmo princípio, a última alteração geralmente é a primeira a ser mostrada.
4. Filas (Queues): FIFO no Controle de Dados
Filas seguem a lógica FIFO (First In, First Out), ou seja, o primeiro a entrar é o primeiro a sair. São perfeitas para situações onde a ordem importa, como em gerenciamento de tarefas ou execução sequencial de processos.
Imagine uma fila de pessoas esperando ser atendidas: quem chega primeiro, é atendido primeiro.
Na programação, isso se aplica a várias operações, como processar etapas de uma compra, onde cada passo segue uma ordem específica (verificação de estoque, criação do pedido, etc.). Filas garantem que tudo aconteça na sequência correta, organizando tarefas complexas de forma eficiente.
5. Árvores: Hierarquia e Organização
Até agora tava tudo tranquilo né? Ouviu árvore bateu o medo? Síndrome do impostor? Calma! Não é esse bicho de 7 cabeças, vou tentar explicar de forma simples, vambora?
Árvores são estruturas de dados que organizam informações em hierarquias, ao contrário de arrays e listas que são lineares. Em uma árvore, cada elemento, ou “nó”, pode ter “filhos”, criando uma estrutura de ramos.
É como as pastas no seu PC: uma pasta principal pode conter várias outras pastas, que por sua vez podem conter mais pastas, e assim por diante. Essa configuração é super útil em bancos de dados e sistemas de arquivos, onde a relação entre os dados não é simplesmente lado a lado, mas sim uma dentro ou abaixo da outra, permitindo organizar e acessar informações de maneira eficiente.
Pense nas pastas do seu PC como um exemplo prático de uma estrutura de árvore em ação, a gente enxerga como pastas, mas é estrutura de árvore, só não estamos vendo isso no código, fez sentido? compreendes?
Dependendo a estrutura da árvore ela pode ser classificada de algumas formas diferentes, então é lógico que vamos explorar mais, afinal aqui é uma mera introdução.
6. Grafos: Modelando Relações Complexas
Eai, a síndrome do impostor veio nas Árvores, ou é agora com Grafos?
Grafos são estruturas que conectam nós (ou pontos de memória com dados) de maneiras flexíveis, diferentemente de arrays ou árvores. Em um grafo, cada nó pode ter várias conexões com outros, sem seguir uma hierarquia fixa como nas árvores.
Isso permite representar relações complexas, como as rotas em um mapa. Imagine usar um app de navegação para ir do ponto A ao B: existem muitos caminhos possíveis, cada um com suas ruas e direções.
Nos grafos, é assim que os nós se relacionam, permitindo explorar diversas conexões até encontrar a informação desejada ou o caminho ideal.
Grafos podem ser complexos, e igual as Árvores existem alguns sub-tipos dependendo o jeito do grafo. Já codou um grafo alguma vez? Já deu manutenção em algum código de grafo?
7. Tabelas de Hash (Hash Tables): Eficiência em Busca
As Hash Tables são estruturas que organizam dados de maneira eficiente, funcionando com um sistema de chave-valor, similar aos Maps.
A grande sacada é que, ao invés de buscar diretamente pela chave, usamos uma função de hash que transforma essa chave em um índice de um array onde o valor está armazenado. Isso pode parecer um passo extra, mas torna a busca super rápida. O truque está em lidar bem com situações onde duas chaves diferentes resultam no mesmo índice, isso é conhecido como colisão.
Graças à rapidez na localização dos dados, as Hash Tables são excelentes para acessar rapidamente informações e manter dados onde a unicidade é crucial.
Se a gente precisa analisar um texto e contar quantas vezes determinada palavra ou número é mencionado no texto, podemos criar uma Hash Table e para cada palavra ou número que queremos contar, nós mapeamos um contador e contamos a frequência que os elementos aparecem no texto.
8. Heaps: Gerenciamento de Dados Prioritários
Heaps são Árvores. O que acontece é que Heaps são um tipo especial de árvores utilizadas para manter elementos organizados de uma certa maneira, facilitando operações como ordenação e gestão de filas de prioridade.
Imagina uma árvore que sempre sabe quem deve estar no topo (no topo da árvore) com base em certas regras — é assim que funcionam os heaps. Eles são super úteis em várias situações, como distribuir tarefas entre servidores ou até recomendar produtos online.
Por exemplo, após comprar algo num site, os produtos recomendados que aparecem podem estar sendo ordenados usando um heap. Essa ordenação se baseia em critérios como popularidade ou relevância, garantindo que a pessoa veja primeiro o que mais interessa.
Heaps são muito conhecidos pela classificação de Max Heap e Min Heap. Outro ponto legal sobre os Heaps é que eles podem ajudar a implementar as Filas de Prioridade, que vou resumir a seguir!
9. Fila de Prioridade (Priority Queue): Ordenação por Prioridade
Combinando características de filas e heaps as Filas de Prioridade são estruturas que organizam elementos não pela ordem de chegada, mas sim pela prioridade de cada um. Imagina uma mistura de filas comuns e heaps, onde o que importa é quão importante é um item, e não quando ele chegou.
Essa característica faz com que itens mais prioritários sejam atendidos primeiro, independentemente de sua posição na fila.
Isso é super útil em várias situações, como sistemas operacionais que precisam decidir qual processo executar primeiro, sistemas de recomendação que mostram conteúdos mais relevantes, ou até em bancos de dados para priorizar consultas. Basicamente, é como ter um atalho sempre que algo realmente importante precisa ser feito.
10. Mapas e Conjuntos (Maps e Sets): Organização e Unicidade
Mapas (Maps) armazenam pares de chave-valor, ou seja, cada bloco de informação tem uma chave e um valor que você determina. Os maps lembram muito as Hash Tables, mas aqui não temos uma hash que aponta para um index e dai encontramos o valor, aqui no Map temos uma chave que aponta diretamente para o valor.
Enquanto “conjuntos” (é meio estranho falar “conjuntos” então vamos usar “Sets”) os Sets não tem uma estrutura de chave-valor igual ao map, em questão de estrutura os sets se assemelham aos Arrays porém com uma regra especial, os sets tem elementos únicos, ou seja, se você tentar incluir um dado que já exista em um Set, você não vai incluir, o Set não vai ter registros “duplicados”.
Maps são muito usados em sistemas de cache, e também por exemplo em mapeamento de dados pra requisições, onde um campo X precisa ser mapeado para um campo Y em uma chamada de API para outros serviços.
Sets são muito usados quando queremos remover itens duplicados de Arrays, ou um outro exemplo seria entrada de dados em uma API, se você tem uma API que recebe um JSON por exemplo, você consegue com um Set garantir que esse JSON não venha com campos duplicados.
11. Árvores de Busca Binária (Binary Search Trees): Busca Otimizada
Árvores de novo… Árvore de busca binária é uma árvore o que acontece aqui é tipo o que acontece nos Heaps: pra existir uma Árvore de Busca Binária as árvores precisam ter algumas características “especiais”.
Vamos chamar "árvore de busca binária" de BST beleza?! Não quero ficar escrevendo “árvore de busca binária” toda hora… O que torna uma árvore uma BST é que, para qualquer nó, todos os valores nos nós da esquerda são menores, enquanto todos os valores nos nós da direita são maiores. Viu? Simples, acabou é isso aí.
Não da pra fugir muito quando falamos de BST, os melhores exemplos são mapas, sistemas de navegação, por exemplo armazenar pontos ou caminhos pra facilitar na busca de pontos e/ou localizações especificas. Da pra citar recursos de auto-completar, que ficam interessantes com uma implementação mais específica que eu cito abaixo no resumo sobre Tries.
12. Trie: Otimização de Buscas por Prefixo
Árvores mais uma vez… "Tries" é uma estrutura de árvore também e o que acontece aqui é tipo o que acontece nas outras que já falei acima. Pra existir uma Trie a árvore precisa ter algumas características "especiais", e no caso da Trie é mais especial ainda. As Tries são árvores focadas em trabalhar com texto.
As Tries armazenam strings de maneira eficiente, geralmente cada nó possui um caractere e o caminho (a estrutura) dos nós da Trie formam uma palavra (string) completa. Um bom exemplo de uso pra esse tipo de estrutura é para dicionários e sistemas complexos de auto-completar, tipo quando você vai no google e coloca a letra A e já aparece um monte de palavras e frases completas que iniciam com a letra A.
13. Grafos de Menor Caminho (Dijkstra, Bellman-Ford): Otimização de Rotas
Não vou mentir não, aqui "o buraco é mais embaixo". Mas se você leu com calma do 1 ao 12, boa parte do que vou falar aqui você já tem alguma base sobre. Grafos de Menor Caminho não são um tipo de Grafo em si, aqui estamos falando de algoritmos que percorrem grafos onde o objetivo é encontrar entre os pontos "A" e "G" o menor caminho.
Não é algo que você vai implementar todo dia, ou mexer todo dia, a não ser que você trabalhe em projetos específicos que lidam com esses tipos de estrutura, tipo GPS, sistemas de navegação em geral, sistemas de comunicação e redes, etc.
14. Algoritmos de Ordenação (QuickSort, MergeSort, BubbleSort): A Arte de Ordenar Dados
Depois de Dijkstra e Bellman-Ford isso aqui vai ser simples né?!
Quando você tem um array e precisa ordená-lo, existem diversos métodos para fazer isso. Embora linguagens de programação já venham com funções de ordenação, às vezes queremos explorar outras formas para entender suas eficiências ou simples curiosidade. Vamos falar sobre três algoritmos de ordenação populares, explicando de maneira simples:
BubbleSort: Um dos métodos mais básicos, onde você compara itens adjacentes e os troca se estiverem na ordem errada. É como percorrer o array, trocando pares até tudo estar ordenado. QuickSort: Rápido e eficiente, usa a técnica de dividir para conquistar. Escolhe um “pivot” e organiza o array de modo que elementos menores fiquem antes e maiores depois, repetindo o processo nos sub-arrays. MergeSort: Também segue a lógica de dividir para conquistar, mas divide o array sempre ao meio. Ordena cada metade e depois as combina de forma ordenada. É um processo de dividir, ordenar e juntar. Veja um exemplo do merge-sort:
E ai, na sua linguagem de programação qual algoritmo é utilizado quando você faz um sort, vc sabe? Sabia que algumas linguagens misturam algoritmos pra ter uma eficiência geral melhor?
15. Busca Binária: Eficiência em Localização
Na busca comum, vamos de um a um em um array. Já na busca binária ela compara o elemento que estamos buscando com o meio do array, e divide o array pela metade. Repetimos esse processo na metade metade do array promissora até achar o item ou acabar as opções. Soa complexo, mas é simples. O ponto chave aqui é que a busca é eficaz em listas ordenadas, só assim ela agiliza a procura.
Um bom exemplo de busca binária é se você tem algum tipo de dicionário e precisa buscar uma palavra. Como esse tipo de coisa geralmente está na ordem, a busca binária pode fazer uma diferença dependendo o tamanho do dicionário. Lembra, o crucial aqui é a lista estar ordenada.
Escolhendo a Melhor Ferramenta para Cada Tarefa
Sabe aquele parafuso que você não consegue apertar ou soltar com a faca? Então, existe a ferramenta certa para cada trabalho, e no nosso mundo de TI temos que escolher a estrutura de dados adequada para cada desafio. Entender essas estruturas e algoritmos é essencial para criar programas práticos.
Qual estrutura de dados você mais usa nos seus projetos ou na sua empresa? Compartilha aí e vamos aprender juntos!