image

Acesse bootcamps ilimitados e +650 cursos

50
%OFF
Article image

LC

Lucas Costa19/07/2024 14:29
Compartilhe

Eficiência de Algoritmos com Estruturas de Dados em C

    Introdução

    A eficiência dos algoritmos está intimamente ligada à escolha e à correta correção das estruturas de dados. Este artigo apresenta os principais aspectos da eficiência de algoritmos em C, com exemplos práticos e explicações claras.

    Complexidade de Tempo e Espaço

    Antes de mergulharmos nas estruturas de dados, é essencial entender a complexidade de tempo e espaço. A complexidade de tempo refere-se ao tempo que um algoritmo leva para ser executado, enquanto a complexidade de espaço refere-se à quantidade de memória necessária.

    Exemplo Simples:

    #include <stdio.h>
    
    // Função que imprime números de 0 a n-1
    void exemploComplexidade(int n) {
      for (int i = 0; i < n; i++) { // O(n) tempo
          printf("%d ", i);
      }
    }
    
    int main() {
      exemploComplexidade(10);
      return 0;
    }
    

    Este exemplo mostra uma complexidade de tempo linear O(n), onde o tempo de execução cresce linearmente com o tamanho da entrada.

    Listas Encadeadas: Flexibilidade com custo

    As listas encadeadas são ótimas para inserções e remoções dinâmicas. No entanto, elas têm um custo adicional em termos de memória devido aos ponteiros.

    Implementação Básica da Lista Encadeada:

    #include <stdio.h>
    #include <stdlib.h>
    
    // Estrutura de nó da lista encadeada
    struct Node {
      int data;
      struct Node* next;
    };
    
    // Função para inserir um novo nó no início
    void inserirNoInicio(struct Node** head_ref, int novo_dado) {
      struct Node* novo_no = (struct Node*)malloc(sizeof(struct Node));
      novo_no->data = novo_dado;
      novo_no->next = (*head_ref);
      (*head_ref) = novo_no;
    }
    
    // Função para imprimir a lista
    void imprimirLista(struct Node* node) {
      while (node != NULL) {
          printf("%d -> ", node->data);
          node = node->next;
      }
      printf("NULL\n");
    }
    
    int main() {
      struct Node* head = NULL;
      inserirNoInicio(&head, 1);
      inserirNoInicio(&head, 2);
      inserirNoInicio(&head, 3);
      imprimirLista(head);
      return 0;
    }
    

    Este código demonstra como inserir nós no início de uma lista encadeada e como imprimir a lista.

    Pilhas: Operações LIFO

    Pilhas são estruturas lineares que oferecem um excelente desempenho para operações de acesso e remoção em uma ordem Last-In-First-Out (LIFO).

    Exemplo Simples de Pilha:

    #include <stdio.h>
    #include <stdlib.h>
    
    #define TAMANHO 100
    
    struct Pilha {
      int top;
      int array[TAMANHO];
    };
    
    // Função para criar uma pilha
    struct Pilha* criarPilha() {
      struct Pilha* pilha = (struct Pilha*)malloc(sizeof(struct Pilha));
      pilha->top = -1;
      return pilha;
    }
    
    // Função para adicionar um item à pilha
    void push(struct Pilha* pilha, int item) {
      pilha->array[++pilha->top] = item;
    }
    
    // Função para remover um item da pilha
    int pop(struct Pilha* pilha) {
      return pilha->array[pilha->top--];
    }
    
    int main() {
      struct Pilha* pilha = criarPilha();
      push(pilha, 10);
      push(pilha, 20);
      printf("%d retirado da pilha\n", pop(pilha)); // Espera-se 20
      return 0;
    }
    Este exemplo mostra a criação de uma pilha, adicionando e removendo itens da pilha.
    

    Árvores Binárias: Equilíbrio entre Espaço e Tempo

    As árvores binárias são estruturas hierárquicas que facilitam buscas rápidas e eficientes. Árvores balanceadas, como AVL, garantem um desempenho ótimo.

    Implementação Básica de Árvore Binária:

    #include <stdio.h>
    #include <stdlib.h>
    
    // Estrutura de nó da árvore binária
    struct Node {
      int data;
      struct Node* left;
      struct Node* right;
    };
    
    // Função para criar um novo nó
    struct Node* novoNo(int data) {
      struct Node* node = (struct Node*)malloc(sizeof(struct Node));
      node->data = data;
      node->left = NULL;
      node->right = NULL;
      return node;
    }
    
    // Função para percorrer a árvore em pré-ordem
    void preOrder(struct Node* node) {
      if (node == NULL) return;
      printf("%d ", node->data);
      preOrder(node->left);
      preOrder(node->right);
    }
    
    int main() {
      struct Node* root = novoNo(1);
      root->left = novoNo(2);
      root->right = novoNo(3);
      root->left->left = novoNo(4);
      root->left->right = novoNo(5);
      preOrder(root); // Espera-se 1 2 4 5 3
      return 0;
    }
    

    Este código cria uma árvore binária simples e percorre a árvore em pré-ordem.

    Tabelas Hash: Acesso Rápido

    Tabelas hash permitem acesso rápido aos dados através de uma função hash, tornando as operações de busca extremamente eficientes.

    Implementação Básica da Tabela Hash:

    #include <stdio.h>
    #include <stdlib.h>
    
    #define TAMANHO 20
    
    // Estrutura de item da tabela hash
    struct Item {
      int chave;
      int valor;
      struct Item* prox;
    };
    
    // Estrutura da tabela hash
    struct TabelaHash {
      struct Item* array[TAMANHO];
    };
    
    // Função hash simples
    int hashCode(int chave) {
      return chave % TAMANHO;
    }
    
    // Função para criar uma tabela hash
    struct TabelaHash* criarTabela() {
      struct TabelaHash* tabela = (struct TabelaHash*)malloc(sizeof(struct TabelaHash));
      for (int i = 0; i < TAMANHO; i++)
          tabela->array[i] = NULL;
      return tabela;
    }
    
    // Função para inserir um item na tabela hash
    void inserir(struct TabelaHash* tabela, int chave, int valor) {
      int hashIndex = hashCode(chave);
      struct Item* novoItem = (struct Item*)malloc(sizeof(struct Item));
      novoItem->chave = chave;
      novoItem->valor = valor;
      novoItem->prox = tabela->array[hashIndex];
      tabela->array[hashIndex] = novoItem;
    }
    
    // Função para buscar um item na tabela hash
    int buscar(struct TabelaHash* tabela, int chave) {
      int hashIndex = hashCode(chave);
      struct Item* item = tabela->array[hashIndex];
      while (item != NULL) {
          if (item->chave == chave)
              return item->valor;
          item = item->prox;
      }
      return -1;
    }
    
    int main() {
      struct TabelaHash* tabela = criarTabela();
      inserir(tabela, 1, 10);
      inserir(tabela, 2, 20);
      printf("Valor para chave 1: %d\n", buscar(tabela, 1)); // Espera-se 10
      return 0;
    }
    

    Este código cria uma tabela hash, insira itens e realize buscas.

    Conclusão

    A escolha da estrutura de dados correta pode fazer uma diferença significativa na eficiência dos algoritmos. Entender as características de cada estrutura e seus casos de uso é essencial para melhorar o desempenho dos programas em C.

    👌Curtiu esse conteúdo ? Ele foi gerado por inteligência artificial, mas foi revisado por alguém 100% Humano, e se quiser se conectar comigo, me siga no Linkedin

    ⚒️Ferrramentas de produção:

    Imagens geradas por: I.A. lexica.art

    Editor de imagem: Power Point

    Conteúdo gerado por: ChatGPT

    Revisões Humanas: Lucas Costa

    #C #Estruturadedados #Programacao

    Compartilhe
    Comentários (0)