Inverter uma Árvore Binária

Muito se fala sobre árvores binárias, especialmente sobre a inversão dessas estruturas. Mas o que isso realmente significa? Quais são as consequências dessa operação? E, antes de tudo, o que é uma árvore binária? Neste artigo, quero começar do princípio e, passo a passo, chegar até o tão mencionado, e muitas vezes temido, processo de inversão de árvores binárias. 😊

Antes que você pergunte, esse não é o tipo de árvore binária que vamos falar nesse ártigo.
A ideia não é se aprofundar no tema, mas sim contextualizá-lo e fornecer uma direção sobre o assunto.

Primeiro, o que é uma Árvore Binária?

Uma árvore binária é uma estrutura de dados hierárquica composta por nós, em que cada nó pode ter no máximo dois filhos: um filho à esquerda e um filho à direita. Essa estrutura é amplamente utilizada em computação para organizar dados de forma eficiente, permitindo buscas, inserções e remoções rápidas.

Principais conceitos de uma árvore binária:

Raiz (root): O primeiro nó da árvore.
Nós (nodes): Elementos da árvore que podem conter um valor e apontar para filhos.
Filho esquerdo e filho direito: Cada nó pode ter até dois filhos.
Folha (leaf): Um nó que não possui filhos.
Altura da árvore: O maior número de arestas (conexões) do nó raiz até uma folha.

Continue reading →

Função Pura: O Que É e Por Que Você Deve Usar?

Se você já programou por um tempo, provavelmente ouviu falar de funções puras. Mas o que exatamente isso significa e por que elas são tão valorizadas?

Elas fazem parte do paradigma da programação funcional, mas não se limitam a ele. Mesmo em código orientado a objetos ou misto, entender e aplicar funções puras pode deixar seu código mais previsível, organizado e fácil de testar.

Vamos tentar entender como elas funcionam?

O Que É uma Função Pura?

Uma função pura segue duas regras simples:

1. Sempre retorna o mesmo resultado para a mesma entrada

Se você passar os mesmos valores como argumento, ela sempre devolverá o mesmo resultado.

2. Não causa efeitos colaterais

Continue reading →

Ordenando Objetos em JavaScript com QuickSort e pesquisando com Binary Search

Se você já precisou ordenar um array de objetos em JavaScript, provavelmente usou o método .sort(). No entanto, entender e implementar algoritmos clássicos de ordenação, como o QuickSort, pode ser muito útil para aprendizado e otimização.

Neste post, vamos criar uma função QuickSort capaz de ordenar um array de objetos de forma dinâmica, depois, usaremos o Binary Search para buscar elementos de forma eficiente.

O que é QuickSort?

QuickSort é um algoritmo eficiente de ordenação que segue o paradigma Dividir para Conquistar. Ele funciona assim:

1. Escolhe um elemento como pivô.

Continue reading →

Code Review Eficaz

Fazer um code review eficaz envolve uma combinação de análise técnica, atenção a boas práticas e comunicação construtiva. Abaixo estão os principais pontos a considerar, incluindo a relevância de conceitos como SOLID, OOP e complexidade de código:

1. O que Verificar no Code Review?

Funcionalidade e Requisitos

  • Alinhamento com os requisitos: O código resolve o problema proposto?
  • Tratamento de casos extremos (edge cases): Ex.: divisão por zero, entradas inválidas, falhas de rede.
  • Impacto em outras partes do sistema: O código quebra funcionalidades existentes?

Qualidade do Código

  • Princípios SOLID (extremamente relevantes):
    • Single Responsibility: Uma classe/função faz apenas uma coisa?
    • Open/Closed: O código está aberto para extensão, mas fechado para modificação?
    • Liskov Substitution: As subclasses podem substituir suas classes base sem quebrar o sistema?
    • Interface Segregation: Interfaces são específicas para o cliente que as usa?
    • Dependency Inversion: Módulos dependem de abstrações, não de implementações?
  • Orientação a Objetos (OOP):
    • Encapsulamento: Dados e comportamentos estão protegidos?
    • Herança vs. Composição: A hierarquia de classes é justificada ou há complexidade desnecessária?
    • Polimorfismo: Há uso adequado de interfaces ou classes abstratas?
  • Programação Funcional (FP)
    • Imutabilidade: O código evita mutações desnecessárias de estado?
    • Funções puras: Funções sempre retornam o mesmo resultado para a mesma entrada?
    • Composição de funções: O código utiliza composição ao invés de sequências complexas de instruções?
    • Evita efeitos colaterais: Há dependências globais ou mutações inesperadas?
    • Uso adequado de Higher-Order Functions: O código se beneficia de funções como mapreduce e filter para evitar loops imperativos?
  • Complexidade:
    • Complexidade Ciclomática: Funções/métodos têm muitas ramificações (if/else, loops)? Valores altos indicam código difícil de testar e manter.
    • Código duplicado: Há violação do DRY (Don’t Repeat Yourself)?
    • Legibilidade: O código é fácil de entender? Nomes de variáveis/métodos são claros?
Continue reading →

Como aplicar SOLID em uma aplicação Angular 18+

A versão 18+ do Angular trouxe uma série de melhorias significativas, como a introdução do modo standalone e um suporte aprimorado à injeção de dependência, facilitando a aplicação dos princípios SOLID de forma mais eficiente e intuitiva.

Para ajudar você a colocar esses conceitos em prática, aqui está um guia com exemplos práticos, mostrando tanto as abordagens incorretas quanto as corretas para cada um dos princípios do SOLID.

1. SRP – Princípio da Responsabilidade Única

“Uma classe deve ter apenas uma razão para mudar e focar exclusivamente em uma única responsabilidade.”
Em outras palavras, cada componente ou classe deve resolver um único problema, evitando a mistura de tarefas não relacionadas. Isso garante código mais coeso, fácil de manter e menos propenso a bugs. Por exemplo, não deve misturar lógica de negócios com chamadas HTTP ou outras tarefas que não sejam diretamente relacionadas à sua função principal.

Exemplo Errado: Componente fazendo tudo

import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-user-profile',
  standalone: true,
  template: `
    <div *ngIf="user">
      <h1>{{ user.name }}</h1>
      <p>Email: {{ user.email }}</p>
    </div>
  `,
})
export class UserProfileComponent {
  user: any;
  private http = inject(HttpClient);

  constructor() {
    this.loadUser();
  }

  loadUser() {
    this.http.get('/api/user').subscribe((data) => (this.user = data));
  }
}
Continue reading →

Um pouco sobre SOLID.

O SOLID é um conjunto de cinco princípios de design de software que visam melhorar a qualidade, a manutenibilidade e a escalabilidade do código. Vamos começar pela letra S, que representa o Princípio da Responsabilidade Única (Single Responsibility Principle – SRP).

S – Princípio da Responsabilidade Única (SRP)

Definição: Uma classe deve ter apenas uma razão para mudar, ou seja, deve ter apenas uma responsabilidade.

Explicação: Esse princípio sugere que uma classe deve ser focada em fazer apenas uma coisa. Se uma classe tem múltiplas responsabilidades, ela se torna mais complexa e difícil de manter. Quando uma classe tem uma única responsabilidade, fica mais fácil de entender, testar e modificar.

Exemplo Prático:

Suponha que você tenha uma classe Pedido que é responsável por gerenciar os detalhes de um pedido e também por salvar o pedido no banco de dados.

class Pedido:
    def __init__(self, id, cliente, itens):
        self.id = id
        self.cliente = cliente
        self.itens = itens

    def calcular_total(self):
        return sum(item['preco'] * item['quantidade'] for item in self.itens)

    def salvar_pedido(self):
        # Lógica para salvar o pedido no banco de dados
        pass
Continue reading →

Como Integrar o Firebase Auth com um autenticador customizado?

Integrar o Firebase Authentication com um autenticador customizado envolve a criação de um sistema de autenticação personalizado que pode ser vinculado ao Firebase. Isso é útil quando você já tem um sistema de autenticação existente e deseja aproveitar os recursos do Firebase, como o Firebase Auth, para gerenciar usuários, tokens, e outras funcionalidades.

Link para a documentação do Firebase.

https://firebase.google.com/docs/auth/web/custom-auth?hl=pt-br

Aqui está um guia passo a passo para integrar o Firebase Auth com um autenticador customizado:

1. Configuração do Firebase

Primeiro, você precisa configurar o Firebase no seu projeto:

  1. Crie um projeto no Firebase Console: Acesse o Firebase Console e crie um novo projeto.
  2. Adicione o Firebase ao seu aplicativo: Siga as instruções para adicionar o Firebase ao seu aplicativo (Android, iOS, ou Web).
  3. Habilite o Firebase Authentication: No Firebase Console, vá para a seção “Authentication” e habilite o método de autenticação “Email/Senha” ou qualquer outro método que você deseja usar.

2. Criar um Autenticador Customizado

Continue reading →

Um Pouco Sobre Complexidade de Código

Volta e meia eu vejo em algumas redes algumas pessoas desenvolvedoras falando sobre, use isso, não use tal, e dizendo que devs juniors já deviam saber disso ou daquilo, porém normalmente isso tem relação a funções extendidas de javascript, python, etc.. mas poucos se atentam a uma questão que pode ser bem crítica, que é a complexidade do código gerado usando essas funções, e nesse artigo gostaria apenas de mostrar exemplos do que são e de pequenas implementações de algoritmos e diversos momentos de complexidade, a notação usada será a Big O
Espero que gostem 🙂

Primeiramente, o que é Big O?

Na ciência da computação, a notação Big O é uma notação matemática que descreve o comportamento limitante de uma função quando o argumento tende a um valor específico ou ao infinito. É membro de uma família maior de notações conhecida como notação Landau, notação Bachmann–Landau, ou notação assintótica.

A notação Big O é usada para classificar algoritmos de acordo com seu tempo de execução ou requisitos de espaço, conforme a entrada de dados aumenta.

Continue reading →

Estruturas de Dados com Java – Playlist Youtube

Essa playlist dos professores Leandro Guarino e Luiz Eduardo Guarino explica muito bem sobre estrutura de dados, não só pra Java, mas para qualquer linguagem, e tbm ensina de maneira bem didática sobre complexidade de código, com exemplo práticos e mostrando quem tem melhor e pior performance seguindo a notação Big O, para pesquisa e ordenação como busca binária, bubble sort, insert sort, entre outras coisas muito interessantes. 
Eu gostei muito, espero que gostem tbm =D

Estruturas de Dados com Java

Firebase Databases Indexes

Realtime Database

Na Realtime Database do Firebase, os índices (indexes) desempenham um papel importante na otimização das consultas. Quando você executa uma consulta em sua base de dados, os índices ajudam a melhorar a eficiência da busca, permitindo que o Firebase recupere os dados de forma mais rápida.

Os índices na Realtime Database do Firebase são um mecanismo que permite que o Firebase armazene os dados de forma mais eficiente, facilitando a consulta e a recuperação dos dados.

Os índices são criados para campos específicos em um documento. Quando um índice é criado, o Firebase armazena uma lista de todos os valores possíveis para esse campo. Essa lista é organizada de forma ordenada, o que facilita a consulta dos dados.

Por exemplo, imagine que você tenha um documento no banco de dados com os seguintes dados:

Continue reading →