Estrutura de Dados para Tecnologia

27 

Full text

(1)

Estrutura de Dados para

Tecnologia

(2)

Roteiro

Definindo recursividade

Funções recursivas

Caso base e passo indutivo

Procedimentos recursivos

(3)

Definindo Recursividade

Um objeto é dito recursivo se ele constituir

parcialmente ou for definido em termos de si

próprio.

Existem várias operações matemáticas que

naturalmente são recursivas, como:

 Multiplicação de números:

 A x B = A + A + A + … + A (B vezes) = A + (A x (B–1))  Se B = 1, não é necessário realizar mais a operação.

 Fatorial:

 fat(5) = 5.4.3.2.1 = 5 x fat(4) = 5 x 4 x fat(3) = …

(4)

Definindo Recursividade

Portanto, há problemas que, por sua própria

definição, são recursivos !

Quando pensamos em uma solução para este

problema, seguindo a natureza recursiva,

então passamos a trabalhar com algoritmos

recursivos e, conseqüentemente, com

(5)

Algoritmos recursivos

Todo algoritmo recursivo deve possuir dois

elementos:

 Uma chamada recursiva, ou seja, uma chamada

ao próprio algoritmo, considerando apenas parte do problema a ser tratado, ou o problema como

um todo. Esta chamada é conhecida como “passo de recursão” ou “passo de indução”.

 Um ponto aonde não é necessário fazer a

chamada ao próprio procedimento. Isto é

(6)

Algoritmos recursivos

 É importante definirmos o caso base, pois de

outro modo o algoritmo não teria um ponto final de execução, ou seja, seria executado

infinitamente. Esse é o principal cuidado que devemos ter ao trabalharmos com algoritmos recursivos.

indutivo

passo

base

caso

(7)

Algoritmos recursivos

Exemplos de algoritmos recursivos:

(8)

Algoritmos recursivos

Os exemplos vistos anteriormente são

funções recursivas, podendo ser

implementados em linguagem C ou qualquer

linguagem de 3

a

geração ou superior.

Para utilizar funções recursivas, basta efetuar

uma chamada à própria função em seu corpo,

como faríamos com qualquer outra função.

Temos que observar se em algum momento

(9)

Algoritmos recursivos

Vantagens:

 Redução do tamanho do código fonte

 Permite descrever algoritmos de forma mais clara

e concisa

Desvantagens

 Redução do desempenho de execução devido ao

tempo para gerenciamento de chamadas

 Dificuldades na depuração de programas

(10)

Funções Recursivas

Implementação da função para calcular

n!

:

unsigned long int fat(int n)

{

if (n == 0) return 1;

else return (n * fat(n-1));

(11)

Funções Recursivas

Comparação entre as duas implementações:

Função não recursiva

Função recursiva

unsigned long int fat(int n);

{

int i,x;

x = 1;

for (i=n; i > 1; i--) x = x*i;

return x;

}

unsigned long int fat(int n)

{

if (n == 0) return 1;

(12)

Funções Recursivas

Chamadas recursivas:

fat(5)

5 * fat(4)

4 * fat(3)

3 * fat(2)

2 * fat(1)

1 1

2 6

(13)

Funções Recursivas

Implementação da função para a

multiplicação de dois números inteiros a e b:

int multiplica(int a,int b) {

if (b < 0)

return -multiplica(a,-b); else

if (b == 0) return 0; else

(14)

Funções Recursivas

Implementação da função para a potenciação

de dois números inteiros a e b:

float potencia(int a,int b) {

if (b < 0)

return (1 / potencia(a,-b)); else

if (b == 0) return 1; else

(15)

Funções Recursivas

 Empilhamento:

 De forma diferente de uma função não recursiva, cada

chamada de uma função recursiva provoca empilhamento de seus dados em memória.

 Todos os dados não globais são armazenados na pilha,

informando o resultado corrente. Quando uma ativação anterior prossegue, os dados da pilha são recuperados

 Isto quer dizer que quanto mais chamadas fazemos a uma

função recursiva, mais recursos de memória são necessários para executar o programa, o que pode torná-lo lento ou

computacionalmente inviável !

 É importante avaliar, então, se é melhor utilizar funções

(16)

Funções Recursivas

 Exercícios:

 Escreva uma função recursiva que calcule o MDC (máximo

divisor comum) de dois números inteiros X e Y.

 Para o cálculo, considere:

 Escreva uma função recursiva que retorne o i-ésimo termo

da série de Fibonacci

         0 ), mod , ( ), , ( 0 , ) , ( Y se Y X Y MDC Y X se X Y MDC Y se X Y X MDC    

 1, 1 2

)

(17)

Procedimentos Recursivos

 Procedimentos recursivos introduzem a possibilidade

de iterações que podem não terminar: existe a necessidade de considerar o problema de

terminação.

 É fundamental que a chamada recursiva a um

procedimento P esteja sujeita a uma condição A, a

qual se torna satisfeita em algum momento da computação.

 Condição de terminação:

 Permite que o procedimento deixe de ser executado

 O procedimento deve ter pelo menos um caso básico para

(18)

Procedimentos Recursivos

 Exemplo: inclusão de um novo nó ao final de uma

lista linear simplesmente encadeada

l

(19)

 Procedimento não recursivo:

void insere_fim(lista *l, int valor) {

lista aux;

node* no = (node *) malloc(sizeof(node)); no->valor = valor;

no->proximo = NULL;

if(*l == NULL) // A lista está vazia ? *l = no;

else {

aux = *l;

while(aux->proximo != NULL) // caminha até o final da lista aux = aux->proximo;

aux->proximo = no; }

(20)

 Procedimento recursivo:

void insere_fim_recursivo(lista *l, int valor) {

node* no;

if(*l == NULL) // Ponto onde o elemento será inserido {

no = (node *) malloc(sizeof(node)); no->valor = valor;

no->proximo = NULL; *l = no;

} else

insere_fim_recursivo(&(*l)->proximo, valor);

(21)

Procedimentos Recursivos

Caminhamento sobre lista encadeada:

Procedimento não recursivo Procedimento recursivo

void mostra_lista(lista l) {

if(l == NULL) // A lista está vazia ? printf("A lista está vazia !\n"); else

{

printf("Elementos da lista: "); while(l != NULL)

{

printf("%5d --> ",l->valor); l = l->proximo;

} }

void mostra_lista_recursivo(lista l)

{

if(l == NULL) // A lista está vazia ?

printf("/");

else

{

printf("%5d --> ",l->valor);

mostra_lista_recursivo(l->proximo);

}

(22)

Procedimentos Recursivos

 Exemplo: Torre de Hanói

 O problema da Torre de Hanói consiste de três pinos: A, B e

C, denominados respectivamente: origem, trabalho e destino, além de n discos de diâmetros diferentes.

 Inicialmente, todos os discos encontram-se empilhados no

pino origem (A), em ordem decrescente de tamanho, de baixo para cima.

 O objetivo é empilhar todos os discos no pino destino (C),

atendendo às seguintes restrições:

 Apenas um disco pode ser removido de cada vez

(23)

Torre de Hanói

Solução do problema:

 Naturalmente, para n > 1, o pino trabalho deverá

ser utilizado como área de armazenamento temporário.

 O raciocínio para resolver o problema é

semelhante a uma prova matemática por indução. Suponha que se saiba como resolver o problema

(24)

Torre de Hanói

 Solução do problema:

 A extensão para n discos pode ser obtida pela realização dos

seguintes passos:

 Resolver o problema da Torre de Hanói para os n-1 discos do topo do pino origem A, supondo que o pino destino seja B e o trabalho seja C;

 Mover o n-ésimo pino (maior de todos) de A para C;

 Resolver o problema da Torre de Hanói para os n-1 discos

localizados no pino B, suposto origem, considerando os pinos A e C como trabalho e destino, respectivamente.

 Ao final destes passos, todos os discos se encontram

empilhados no disco C e as duas restrições foram satisfeitas.

(25)

Torre de Hanói

Procedimento Recursivo

void MoverDisco(int *origem, int *destino)

{

(*origem)--;

(*destino)++;

}

void Hanoi(int discos,int *A,int *C,int *B)

{

if (discos > 0) {

Hanoi(discos-1,A,B,C);

MoverDisco(A,C);

(26)
(27)

Algoritmos Recursivos

Para criar algoritmos recursivos:

 Defina pelo menos um caso básico (condição de

terminação);

 Quebre o problema em problemas menores,

definindo o(s) caso(s) com recursão(ões)

 Faça o teste de finitude, isto é, certifique-se de

Figure

Updating...

References

  1. Funcionamento:
Download now (27 pages)