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

Um pouco sobre o IONIC framework

O Ionic é um framework de código aberto para o desenvolvimento de aplicativos móveis híbridos e progressivos(PWA). Ele permite criar aplicativos para várias plataformas, como iOS, Android e web, usando tecnologias web como HTML, CSS e JavaScript.

O IONIC utiliza o Capacitor como base para acessar os recursos nativos dos dispositivos, como câmera, GPS, contatos, etc. Além disso, o IONIC usa o TypeScript e o Angular para prover uma solução de mais alto nível em termos de código e arquitetura. O IONIC também possui uma interface de linha de comando (CLI) que facilita a criação, o gerenciamento e o teste dos aplicativos.

Principais recursos e benefícios do Ionic:

  1. Desenvolvimento multiplataforma: Com o Ionic, é possível criar aplicativos para várias plataformas, como iOS, Android e web, a partir de um único código-base. Isso economiza tempo e esforço, evitando a necessidade de desenvolver aplicativos separados para cada plataforma.
  2. Componentes estilizados: O Ionic possui uma biblioteca extensa de componentes de interface de usuário pré-construídos, como botões, listas, menus, abas, sliders e muito mais. Esses componentes são estilizados e prontos para uso, permitindo criar interfaces de usuário atraentes e consistentes em seus aplicativos.
  3. Facilidade de uso: O Ionic é projetado para ser fácil de usar, especialmente para desenvolvedores web que estão familiarizados com tecnologias como HTML, CSS e JavaScript. Ele utiliza conceitos familiares, como a criação de páginas e o uso de componentes, facilitando a transição para o desenvolvimento de aplicativos móveis.
  4. Integração com Angular: O Ionic é construído em cima do Angular, o que significa que você pode aproveitar os recursos poderosos do Angular para criar aplicativos mais robustos. Isso inclui recursos como gerenciamento de estado, injeção de dependência e a capacidade de criar componentes reutilizáveis.
  5. Acesso a recursos nativos: Com o Ionic, é possível acessar recursos nativos do dispositivo, como câmera, GPS, armazenamento local e notificações push, usando plugins específicos do Ionic ou do Capacitor (uma biblioteca de abstração para acessar recursos nativos).
  6. Comunidade e ecossistema: O Ionic possui uma comunidade ativa e um ecossistema robusto de plugins, temas e bibliotecas adicionais. Isso significa que você pode encontrar suporte, recursos adicionais e soluções para desafios comuns ao desenvolver aplicativos com o Ionic.
Continue reading →