Algoritmos e Estrutura de Dados II Revisão: Tipo Abstrato de Dados Recursividade Profa Karina Oliveira [email protected] Introdução • Estudo das estruturas de dados envolve dois objetivos complementares: – Identificar e desenvolver estruturas de dados, determinando os tipos de problemas que podem ser resolvidos com estas estruturas; – Estudar formas de implementação das estruturas de dados nos computadores. Algoritmos e Estrutura de Dados II - Karina Oliveira 2 Tipo Abstrato de Dados (TAD) • TAD constitui um modelo matemático que pode ser empregado para modelar e solucionar problemas do mundo real – Conjunto de valores e operações que podem ser aplicadas sobre estes valores • TAD não considera: – Representação dos valores na memória do computador (organização dos bits); – Tempo necessário para aplicar as operações. Algoritmos e Estrutura de Dados II - Karina Oliveira 3 Tipo Abstrato de Dados (TAD) • Aspecto mais relevante na programação é a implementação do TAD em um tipo de dado concreto • Exemplo: O tipo abstrato dos inteiros – Dados: {...,-3,-2,-1,0,1,2,3,...} – Operações: {+,-,*,/} • Obs.: Apenas um subconjunto dos números inteiros pode ser implementado. Algoritmos e Estrutura de Dados II - Karina Oliveira 4 Recursividade - Fundamentos • Algoritmo que para resolver um problema divide-o em subproblemas mais simples estas soluções requerem a aplicação dele mesmo • Recursividade Direta ou Indireta R ≡ [C, R] R1 ≡ [C, R2] R2 ≡ [C, R3] R3 ≡ [C, R4] ... Rn ≡ [C, R1] Algoritmos e Estrutura de Dados II - Karina Oliveira 5 Recursividade - Fundamentos • Todo algoritmo recursivo deve terminar após ter executado uma quantidade finita de passos – Deve existir uma expressão lógica que deverá ser falsa em algum instante permitindo que a recursão termine – R ≡ [C, T→R], rotina R só vai ser chamada se o teste T for verdadeiro Algoritmos e Estrutura de Dados II - Karina Oliveira 6 Recursividade - Fundamentos • Rotina recursiva deve ter: – Solução trivial: não usa recursão. Resolvida por um conjunto de comandos C. – Solução geral: parte do problema que é em essência igual ao original, porém menor. A solução, neste caso, pode ser obtida por uma chamada recursiva R(x-1) Algoritmos e Estrutura de Dados II - Karina Oliveira 7 Recursividade - Fundamentos • Rotina recursiva deve ter: – Exemplo: Fatorial de número natural • Solução trivial: 0! = 1 {dada por definição} • Solução geral: n! = n*(n-1)! {requer aplicação da rotina para (n-1)!} – Código Exemplo: int fatorial (int n){ if (n == 0) return 1; else return (n * fatorial(n - 1)); } Algoritmos e Estrutura de Dados II - Karina Oliveira 8 Quando aplicar Recursão? • Deve-se analisar o problema para verificar se é válido usar recursão • Quando bem utilizada a recursão torna o algoritmo elegante, simples e conciso • Porém solução iterativa é mais eficiente, pois cada vez que a rotina é chamada, todas as variáveis locais são recriadas • Portanto, deve-se decidir entre recursão e iteração Algoritmos e Estrutura de Dados II - Karina Oliveira 9 Quando aplicar Recursão? 1) Eliminar recursão de cauda • • Definição: recursividade se encontra no final do código criando um “looping” Exemplo: int fatorial (int n){ if (n == 0) return 1; else return (n * fatorial(n - 1)); } • Nesse caso, a recursão é menos eficiente que a iteração. Algoritmos e Estrutura de Dados II - Karina Oliveira 10 Quando aplicar Recursão? 1) Eliminar recursão de cauda • • Deve-se usar uma estrutura de repetição que esteja condicionada à expressão de teste usada na versão recursiva Exemplo do fatorial: int fatorial (int n){ int result = 1; while(n > 0){ result = result * n; n = n – 1; } return result; } Algoritmos e Estrutura de Dados II - Karina Oliveira 11 Quando aplicar Recursão? • Pilhas e Rotinas Recursivas – Controle de chamadas e retornos de rotinas é feito através de uma pilha criada e mantida, automaticamente, pelo sistema – Sempre que uma rotina é evocada, não apenas o endereço de retorno é empilhado, mas todas as suas variáveis locais são recriadas na pilha Algoritmos e Estrutura de Dados II - Karina Oliveira 12 Quando aplicar Recursão? • Pilhas e Rotinas Recursivas – fatorial(4) 0 3 n 4 n 4 n 1 1 2 2 2 3 3 3 4 n 4 n 4 – Obs.: Em cada momento, apenas a última variável n criada pode ser acessada! Algoritmos e Estrutura de Dados II - Karina Oliveira 13 Pilhas e Rotinas Recursivas • Relação entre pilhas e recursão struct LstOrd{ int num; struct LstOrd *prox; } //Versão com recursividade void show (LstOrd *lista){ if (lista != null){ show(lista->prox); cout << (lista->num); } } Algoritmos e Estrutura de Dados II - Karina Oliveira 14 Pilhas e Rotinas Recursivas • Relação entre pilhas e recursão // Versão com pilha void show_pilha (LstOrd *lista){ int num; Pilha p; push: insere um novo init(P); elemento no topo da pilha while (lista != null){ pop: remove um elemento do push(P, lista->num); topo da pilha lista = lista->prox; init: inicializa a pilha no } estado vazia while(!isEmpty(P)){ isEmpty: verifica se a pilha num = pop(P); está vazia cout << num; } } Algoritmos e Estrutura de Dados II - Karina Oliveira 15 Quando aplicar Recursão? • Eliminar recursão utilizando em seu lugar comandos de repetição e, eventualmente pilhas – Nova versão se torna mais rápida que a recursiva • O uso de muitas pilhas e muitos comandos de repetição, tornará o código muito complexo – Versão recursiva nesse caso, é mais clara, simples e concisa Algoritmos e Estrutura de Dados II - Karina Oliveira 16 Exercício • Escreva uma função não recursiva, ou seja, iterativa para a seguinte função: int func (int i){ int result; if (i > 1){ result = i + func(i-1); }else{ result = 1; } return result; } Algoritmos e Estrutura de Dados II - Karina Oliveira 17