Explorar E-books
Categorias
Explorar Audiolivros
Categorias
Explorar Revistas
Categorias
Explorar Documentos
Categorias
Disciplina do 1o módulo do
Curso de Ciência da Computação
da Universidade Federal de Lavras
Agosto de 2002
1
Professora Auxiliar do Departamento de Ciência da Computação da UFLA
1
ÍNDICE
1. Introdução ......................................................................................................................................... 2
1.1. Necessidade do uso da lógica ...................................................................................................... 2
1.2. Conceitos Básicos......................................................................................................................... 2
1.3. Conceito de Algoritmo................................................................................................................... 2
2. PORTUGOL...................................................................................................................................... 3
2.1. Definição de Variáveis – Tipos Básicos........................................................................................ 3
2.2. Comandos Básicos ....................................................................................................................... 4
2.3. Blocos e Comandos Básicos de Controle..................................................................................... 4
2.4. Outras estruturas de repetição...................................................................................................... 7
2.5. Definição de novos tipos de dados ............................................................................................... 8
2.6. Estruturas Condicionais Encadeadas ........................................................................................... 8
3. Ordenação ........................................................................................................................................ 9
3.1. Ordenação de Vetores .................................................................................................................. 9
3.1.1. Ordenação por inserção ........................................................................................................ 9
3.1.2. Ordenação por seleção........................................................................................................ 10
4. Matrizes .......................................................................................................................................... 11
4.1. Matrizes Bidimensionais ............................................................................................................. 11
5. Registros......................................................................................................................................... 12
5.1. Registro com Vetor ..................................................................................................................... 12
5.2. Conjuntos de Registros............................................................................................................... 13
6. Arquivos .......................................................................................................................................... 13
6.1. Organização de Arquivos............................................................................................................ 14
6.1.1. Organização Seqüencial...................................................................................................... 14
6.2. Organização Direta ..................................................................................................................... 15
7. Modularização ................................................................................................................................ 17
7.1. Benefícios da modularização ...................................................................................................... 18
7.2. Ferramentas para modularização ............................................................................................... 18
7.3. Modos de transferência de parâmetros ...................................................................................... 19
8. Recursividade ................................................................................................................................. 19
8.1. Exemplo de problema recursivo ................................................................................................. 20
8.2. Recursão × Iteração.................................................................................................................... 20
8.3. Exemplos mais famosos de problemas recursivos..................................................................... 20
9. Apontadores ................................................................................................................................... 21
10. Listas Lineares................................................................................................................................ 22
10.1. Operações em listas lineares .................................................................................................. 22
10.2. Implementação de listas lineares ............................................................................................ 23
2
1. INTRODUÇÃO
1.1. NECESSIDADE DO USO DA LÓGICA
A lógica é a ciência que estuda as leis e os critérios de validade que regem o pensamento e a
demonstração, ou seja, ciência dos princípios formais do raciocínio. A lógica é usada no dia a dia das
pessoas que trabalham com computação para solucionar problemas de forma eficiente.
Um algoritmo é uma norma executável para estabelecer um certo efeito desejado, que na
prática será geralmente a obtenção de uma solução a um certo tipo de problema.
Exercício: escreva um algoritmo para se trocar um pneu furado. Assuma que estão disponíveis um
macaco e um estepe em boas condições.
Seguindo com o exemplo da troca de lâmpadas, vamos supor que há a possibilidade de que a
escada disponível não seja alta suficiente para alcançar a lâmpada e que, neste caso, gostaríamos
prever este possível erro. Poderíamos reescrever o algoritmo desta forma:
“pegar uma lâmpada nova no armário”;
“pegar a escada na área de serviço”;
“subir na escada com a lâmpada nova na mão”;
3
se “for possível alcançar a lâmpada a ser trocada” então
“retirar a lâmpada queimada”;
“colocar a lâmpada nova”;
“descer da escada”;
“guardar a escada”;
Outro caso: supondo que havia várias lâmpadas para serem trocadas na casa. Poderíamos
reescrever o algoritmo desta forma:
“pegar todas as lâmpadas novas no armário”;
“pegar a escada na área de serviço”;
enquanto “existirem lâmpadas novas disponíveis” faça
“subir na escada com uma lâmpada nova na mão”;
se “for possível alcançar a lâmpada a ser trocada” então
“retirar a lâmpada queimada”;
“colocar a lâmpada nova”;
“descer da escada”;
“guardar a escada”;
2. PORTUGOL
A partir de agora será introduzida uma linguagem de expressão de algoritmos, o PORTUGOL.
Serão apresentadas a sintaxe e a semântica dos comandos da linguagem. PORTUGOL é uma
pseudolinguagem de programação utilizada para obter uma notação para algoritmos, a ser usada na
definição, na criação, no desenvolvimento e na documentação de um programa.
O objetivo não é criar mais uma linguagem de programação, por isso as regras não precisam
ser seguidas de forma muito rígida. Considerando o PORTUGOL, a sintaxe é definida e a forma
apresentada e aceita como padrão. Para cada declaração e/ou comando a semântica deve ser
explicada.
letra
letra
dígito
No PORTUGOL existem quatro tipos básicos, isto é, tipos básicos de dados que podem ser
utilizados: INTEIRO, REAL, CARACTER e LÓGICO.
Uma variável pode ser entendida como um local onde se pode colocar qualquer valor do
conjunto de valores possíveis do tipo básico associado. O nome da variável é o identificador tal como
definido anteriormente. Por exemplo:
SOMA
Variável
SOMA
inteiro : identificador ;
real
caracter
lógico
O comando de ATRIBUIÇÃO é utilizado para atribuir um valor a uma variável. Para isso usa-
se o símbolo ←, conforme a seguinte sintaxe:
identificador ← expressão ;
A notação usada para expressões é basicamente uma forma linear comumente usada na
matemática, que pode conter operadores:
ARITMÉTICOS: +, -, /, *, raiz( ), **, sen( ), cos( ), mod, div,...
LÓGICOS: e, ou, não ( Λ, V, )
RELACIONAIS: =, ≠, >, ≥ (ou >=), <, ≤ (ou <=)
Um bloco pode ser definido como um conjunto de comandos com uma função bem definida.
Serve também para definir os limites onde as variáveis declaradas em seu interior são conhecidas.
Exemplo:
início
fim
Uma seqüência simples é um conjunto de comandos separados por ponto e vírgula (;), que
serão executadas numa seqüência linear de cima para baixo.
Exemplo:
comando1;
comando2;
comando3;
...
comandoN;
Quando a ação a ser executada depender de uma inspeção (teste), teremos uma alternativa,
ou estrutura condicional, simples ou composta.
5
Nos comandos apresentados, < condição > é qualquer expressão cujo resultado seja falso ou
verdadeiro.
Exercício: Qual será o valor final das variáveis A e B depois da execução do seguinte algoritmo?
início
inteiro: A, B;
A ← 1;
B ← 2;
se A > B
então
A ← 5;
senão
A ← 10;
fim se;
fim;
Enquanto o valor da < condição > for verdadeiro, as ações dos comandos são executadas e
quando se tornar falso, o comando é abandonado. Se já da primeira vez o resultado é falso, os
comandos não são executados.
6
Exercício: Qual será o valor final das variáveis declaradas no seguinte algoritmo, depois de sua
execução?
início
inteiro: A, B, C, I;
A ← 1;
B ← 1;
I ← 1;
enquanto I < 10 faça
C ← A + B;
A ← B;
B ← C;
I ← I + 1;
fim enquanto;
fim;
Até agora todos os valores calculados pelos algoritmos foram gerados e permaneceram na
memória. Para obter ou para fornecer dados ao ambiente exterior ao algoritmo, por exemplo do teclado
e para o vídeo, é preciso utilizar comandos de entrada e saída. O comando de entrada é leia e o
comando de saída é imprima, e suas sintaxes são apresentadas a seguir.
( ) ;
leia identificador
imprima ( identificador ) ;
expressão
caracter
Exercício: Escreva o algoritmo que realize a multiplicação de dois números inteiros, utilizando apenas o
operador da soma (+).
Serão apresentadas a seguir outras duas estruturas de repetição que também podem ser
utilizadas na construção de algoritmos usando o PORTUGOL, são elas: repita e para.
A estrutura de repetição repita difere da enquanto no que diz respeito ao momento em que o
teste da condição é submetido.
repita
comando1;
comando2;
...
comandoN;
até < condição >;
Na estrutura enquanto o teste é realizado antes da execução do primeiro loop, ou seja, pode
ser que os comandos não sejam realizados sequer uma vez, caso a condição seja falsa já na primeira
vez em que foi testada. Já na repita os comandos sempre serão executados pelo menos uma vez, até
que a condição seja testada, no final da estrutura.
A estrutura de repetição para difere das estruturas enquanto e repita, pois utiliza uma variável
de controle, que atua como um contador de repetições.
para I de 1 até 10 passo 1 faça
comando1;
comando2;
...
comandoN;
fim para;
Observando o exemplo, percebe-se que foi utilizada uma variável do tipo inteiro (I), que deve
ter sido declarada anteriormente. Esta estrutura irá executar os comandos 10 vezes, pois possui I
variando automaticamente de 1 em 1 (passo 1) até 10, ou seja, não é necessário fazer o incremento
deste dentro da estrutura de repetição.
Exercício: Avalie os algoritmos construídos até o presente momento e, quando achar viável, substitua a
estrutura de repetição enquanto pela estrutura repita ou para, a fim de melhorar o seu desempenho.
Nem sempre os tipos básicos (inteiro, real, caracter e lógico) são suficientes para exprimir
estruturas de dados em algoritmos. Daí a necessidade de novos tipos de dados serem criados. Um
destes tipos é o vetor.
No PORTUGOL a criação de um vetor segue as especificações:
tipo nome_do_tipo = vetor [li:ls] <tipo_básico>;
É dado um nome_do_tipo ao novo tipo de vetor criado, onde li é o limite inferior e ls é o limite
superior de um intervalo que define o tamanho do vetor (valores inteiros), e tipo_básico é um dos tipos
básicos já conhecidos. Esta especificação apenas indica um modelo para a criação de variáveis deste
novo tipo. Para efetivar esta estrutura dentro do algoritmo, é necessário declará-la dando um nome a
variável que será criada segundo o modelo especificado.
Exemplo: um vetor que armazena as notas de todos os alunos de uma turma com 25 alunos.
tipo v = vetor [1:25] real;
v: NOTAS;
O número de elementos de um vetor é dado por ls-li+1. Isto significa que as posições do vetor
são identificadas a partir de li, com incrementos unitários até ls.
li li+1 li+2 … ls
Cada elemento de um vetor é tratado como se fosse uma variável simples. Para referência a um
elemento do vetor utiliza-se o nome do vetor e a identificação do elemento (índice) entre colchetes ([ ]).
o
Por exemplo, se quisermos atribuir o valor de uma 45 a nota do 6 aluno (que é identificado
pelo índice 6 do vetor de notas): NOTAS [6] ← 45;
Exemplo: O que será impresso no algoritmo abaixo?
início
inteiro: I;
tipo vc = vetor [1:7] caracter;
vc: DIAS;
DIAS [1] ← “domingo”;
DIAS [2] ← “segunda-feira”;
DIAS [3] ← “terça-feira”;
DIAS [4] ← “quarta-feira”;
DIAS [5] ← “quinta-feira”;
DIAS [6] ← “sexta-feira”;
DIAS [7] ← “sábado”;
para I de 1 até 7 passo 2 faça
imprima (DIAS [I]);
fim para;
fim.
Exercício: Um professor de uma turma com 30 alunos quer armazenar as notas de seus alunos em um
vetor e depois calcular a média geral da turma. Escreva um algoritmo que solucione este problema
usando uma estrutura de vetor.
3. ORDENAÇÃO
A atividade de ordenação é o processo de rearranjo de um certo conjunto de objetos de
acordo com um critério (ordem) específico. O objetivo da ordenação é facilitar a localização dos
membros de um conjunto de dados.
Estes três princípios serão examinados e comparados. Os exemplos operam sobre a variável A,
cujos componentes serão ordenados e se referem a um vetor de inteiros de tamanho variável (N),
definido como se segue:
inteiro: N;
tipo vet = vetor [1:N] inteiro;
vet: A;
Em cada passo, iniciando-se com i=2 e incrementando-se i de uma em uma unidade, o i-ésimo
elemento da seqüência vai sendo comparado com os elementos anteriores e, se for o caso, retirado e
inserido na posição apropriada.
O processo de ordenação por inserção será mostrado em um exemplo, em que são ordenados
oito números (N=8) escolhidos aleatoriamente. O algoritmo deve fazer o seguinte:
para I de 2 até N faça
X ← A[I];
inserir X no local adequado em A[1]...A[I]
fim para
Valores iniciais 44 55 12 42 94 18 06 67
i=2 44 55 12 42 94 18 06 67
i=3 12 44 55 42 94 18 06 67
i=4 12 42 44 55 94 18 06 67
i=5 12 42 44 55 94 18 06 67
i=6 12 18 42 44 55 94 06 67
10
i=7 06 12 18 42 44 55 94 67
i=8 06 12 18 42 44 55 67 94
Para encontrar o local apropriado do elemento observado é conveniente utilizar, de modo
alternado, operações de comparação e de movimentação, examinando X, e comparando-o com o
elemento A[J], e então efetuando ou a inserção de X ou a movimentação do elemento A[J], e
prosseguindo-se para a esquerda no tratamento dos outros elementos.
Para isso será necessário testar duas condições distintas que causam o término deste
processo de análise:
• Um elemento A[J] é encontrado com um elemento de valor menor do que o seu
• A extremidade esquerda é atingida
É um caso típico de uma repetição com duas condições de término, que conduz a utilização de
um elemento sentinela (para armazenar temporariamente o valor de algum elemento que está sendo
analisado). Para isso, será utilizada uma posição do vetor A como sentinela, o A[0] que receberá o valor
de X.
início
inteiro: I, J, N, X;
tipo vet = vetor [0:N] inteiro;
vet: A; {supondo que o vetor A tenha sido preenchido}...
para I de 2 até N faça
X ← A[I];
A[0] ← X;
J ← I;
enquanto X < A[J-1] faça
A[J] ← A[J-1];
J ← J-1;
fim enquanto;
A[J] ← X;
fim para;
fim;
Valores iniciais 44 55 12 42 94 18 06 67
06 55 12 42 94 18 44 67
06 12 55 42 94 18 44 67
06 12 18 42 94 55 44 67
06 12 18 42 94 55 44 67
06 12 18 42 44 55 94 67
06 12 18 42 44 55 94 67
06 12 18 42 44 55 67 94
início
inteiro: I, J, N, K;
tipo vet = vetor [1:N] inteiro;
vet: A; {supondo que o vetor A tenha sido preenchido}...
para I de 1 até N–1 faça
K ← I;
X ← A[I];
para J de I+1 até N faça
se A[J] < X então
K ← J;
11
X ← A[K];
fim se;
fim para;
A[K] ← A[I];
A[I] ← X;
fim para;
fim;
4. MATRIZES
Uma matriz é uma estrutura de dados homogênea, ou seja, todos os elementos de uma matriz
são do mesmo tipo. Um vetor é uma matriz unidimensional, a partir de agora serão apresentadas
matrizes com mais de uma dimensão.
A forma mais comum de trabalhar com matrizes é utilizando duas dimensões, apesar de que
em alguns casos possa ser necessário trabalhar com mais de duas. Uma matriz bidimensional é
composta por linhas e colunas. As linhas podem ser consideradas como a primeira dimensão e as
colunas a segunda dimensão. É preciso definir o tamanho de cada uma dessas dimensões, ou seja, o
número de linhas e o número de colunas que esta matriz deverá possuir.
TABELA 1 2 3 4 5
1
2
3
4
5
6
7
8
Seja uma matriz a representação das notas obtidas pelos alunos em uma determinada disciplina. A
quantidade de linhas deverá ser equivalente ao número de alunos, neste caso 25. Cada coluna deverá
conter o valor de uma das avaliações de cada aluno, neste caso são 3 avaliações. O algoritmo deve
preencher a matriz com as notas.
início
inteiro: I, J;
tipo m = matriz [1..25, 1..3] real;
m: NOTAS;
para J de 1 até 3 faça
imprima (“Digite as notas referentes a prova”, J);
para I de 1 até 25 faça
leia (NOTAS [I, J]);
fim para;
fim para;
fim.
12
Exercício: Escreva um algoritmo que receba as notas referentes a três avaliações realizadas por 25
alunos, e as armazene numa matriz, juntamente com a média total obtida pelo aluno. Sabendo que: as
duas primeiras avaliações têm peso de 35 cada uma e a terceira tem peso de 30 pontos. Além disso,
para cada média total deve ser enviada uma mensagem informando se o aluno foi aprovado (>=50) ou
reprovado (<50) e qual foi a porcentagem da turma aprovada.
5. REGISTROS
O registro é um conjunto de dados logicamente relacionados e é uma das principais
estruturas de dados. Um registro consiste em trabalhar vários dados de tipos diferentes em uma mesma
estrutura e por isso é considerado heterogêneo.
Para se declarar um registro segue-se a sintaxe:
tipo <identificador> = registro
<lista de campos e seus tipos>
fim registro;
Por exemplo, seja um registro constituído dos campos referentes aos dados de um aluno da
universidade, tais como, número de matrícula, nome completo, idade, turma, período em que se
encontra e média geral. O algoritmo para criar o registro e ler os dados referentes a um aluno poderia
ser o seguinte:
início
tipo reg_aluno = registro
caracter: MAT
caracter: NOME
inteiro: IDADE
caracter: TURMA
inteiro: PERIODO
real: MEDIA
fim registro;
reg_aluno: ALUNO;
leia (ALUNO.MAT);
leia (ALUNO.NOME);
leia (ALUNO.IDADE);
leia (ALUNO.TURMA);
leia (ALUNO.PERIODO);
leia (ALUNO.MEDIA);
fim
Um ou mais campos de um registro pode ser do tipo vetor. A construção do registro é feita da
mesma forma, porém o vetor a ser utilizado em sua estrutura deve ser declarado anteriormente.
Exemplo:
início
tipo vet = vetor [1..6] real;
tipo reg_aluno = registro
caracter: MAT
caracter: NOME
inteiro: IDADE
caracter: TURMA
inteiro: PERIODO
vet: NOTAS
fim registro;
reg_aluno: ALUNO;
inteiro: I;
imprima (“Digite a matrícula do aluno”);
leia (ALUNO.MAT);
imprima (“Digite o nome do aluno”);
leia (ALUNO.NOME);
13
imprima (“Digite a idade do aluno”);
leia (ALUNO.IDADE);
6. ARQUIVOS
As operações básicas que podem ser feitas em um arquivo através de um algoritmo são:
obtenção de um registro do arquivo, inserção de um novo registro, modificação ou exclusão de um
registro.
A disposição (organização) de registros no arquivo pode favorecer determinadas operações
em detrimento de outras. Conhecendo a organização, o projetista de algoritmos pode escolher aquela
que seja mais adequada à solução do seu problema em termos de eficácia e eficiência.
Basicamente, existem duas possibilidades de organização de arquivos:
• Seqüencial – na qual os registros são obtidos ou inseridos no arquivo em ordem seqüencial;
• Direta – em que o acesso do registro é feito de forma direta através do uso de um
identificador para o registro.
O fato de o arquivo ser armazenado em uma memória secundária o torna independente de
qualquer algoritmo, ou seja, ele pode ser criado, consultado, processado e até mesmo removido por
algoritmos distintos.
Sendo o arquivo uma estrutura fora do ambiente do algoritmo, para que este tenha acesso
aos dados do arquivo são necessárias as operações de leitura e escrita de registros no arquivo.
No algoritmo, o arquivo deve ser declarado e aberto antes que o acesso possa ser feito. No
final do algoritmo, ou quando houver necessidade, o arquivo deve ser fechado.
A declaração de um arquivo é feita através da especificação:
arquivo <organização> de NOME-DO-REGISTRO: NOME;
Exemplo:
tipo reg_aluno = registro
...
fim registro;
reg_aluno: ALUNO;
arquivo seqüencial de ALUNO: ALUNOS;
A declaração do arquivo é a definição, para o algoritmo, do modelo e dos nomes que estarão
associados à estrutura de dados. A associação deste modelo ao arquivo físico é feita no algoritmo com
um comando de abertura:
abra NOME-DO-ARQUIVO <tipo de utilização>;
onde o tipo de utilização pode ser para leitura, escrita ou ambos.
Exemplos:
abra ALUNOS leitura;
abra ALUNOS escrita;
abra ALUNOS;
Os formatos para leitura e escrita de um arquivo são dependentes do seu tipo de organização.
Observação: Para escrever dados numa próxima posição do arquivo, deve-se incluir “próximo” ao
comando de escrita. Por exemplo, escreva próximo ARQUIVO.REGISTRO;
Observação: Existe uma variável lógica pré-definida para cada arquivo chamada FDA (Fim De Arquivo),
que indica se um arquivo chegou ou não ao seu último arquivo.
Exercício 2: Crie um algoritmo para entrar com dados, a partir do teclado, de um novo funcionário no
arquivo A.
Através de funções internas ao computador, cada registro será alocado em uma posição
univocamente determinada pela chave escolhida, como mostra a tabela abaixo:
Para se ter acesso a um registro, basta efetuar-se a leitura no arquivo usando a chave, no
caso o número da matrícula, desejada. Não há necessidade do algoritmo fazer nenhum tipo de
pesquisa.
O mecanismo de gerência do arquivo direto no computador é capaz de associar a chave ao
registro procurado. Caso a chave não exista, uma condição de chave inválida (INV) poderá ser testada.
A escolha da chave é feita pelo usuário no momento da criação do arquivo de organização direta e, em
geral, é um dos campos do registro.
Observações importantes:
• Cada registro deverá ser gravado usando sua chave.
• Não pode haver registros usando a mesma chave (é única).
As operações de leitura e escrita num arquivo de organização direta são indicadas nos
algoritmos pelos seguintes comandos:
leia item [chave] NOME-ARQUIVO.NOME-REGISTRO;
e
escreva item [chave] NOME-ARQUIVO.NOME-REGISTRO;
• Contendo os nomes dos clientes que compraram mais de R$500,00 (valor total da compra).
• Contendo os códigos das peças que venderam mais de 100 unidades para um cliente, no mês
de junho de 2002.
2. No mesmo arquivo da questão anterior, faça a busca de uma determinada peça por seu código.
Implemente com os dois tipos de organização de arquivos e discuta os problemas relacionados a estas
diferentes soluções.
7. MODULARIZAÇÃO
Exemplo – Seja um algoritmo para calcular o salário líquido de um empregado, com as seguintes
etapas:
início
Leia os dados do empregado
Determine o salário
Escreva o salário
fim.
Módulo Principal
A independência do módulo permite uma manutenção mais simples e evita efeitos colaterais
no restante do algoritmo;
• A elaboração do módulo pode ser feita independentemente e em época diferente do
restante do algoritmo;
• Testes e correções dos módulos podem ser feitos separados;
• Um módulo pode ser utilizado em outros algoritmos que requeiram o mesmo processamento
por ele executado.
Criação de sub-rotina
subrotina NOME (lista-de-parâmetros-formais)
declarações dos objetos locais a sub-rotina
comandos da sub-rotina
fim subrotina;
Chamada da sub-rotina
NOME (lista-de-parâmetros-atuais);
Criação de função
função tipo NOME (lista-de-parâmetros-formais)
declarações dos objetos locais a função
comandos da função
fim função;
Chamada da função
NOME (lista-de-parâmetros-atuais);
Como esta função irá retornar um valor, este pode ser atribuído a alguma variável, contanto
que esta seja de tipo compatível.
A ← NOME (lista-de-parâmetros-atuais);
8. RECURSIVIDADE
Um objeto é dito recursivo se ele consistir parcialmente ou for definido em termos de si
próprio.
20
Uma função é recursiva quando no corpo dessa função existe uma chamada a si própria,
podendo utilizar os mesmos parâmetros de entrada (correndo riscos de provocar um ciclo infinito) ou
outros.
Imagine que temos um monte de pregos e queremos saber quantos são. Se pegarmos num
prego, sabemos que temos um prego, mas não sabemos quantos ainda existem no monte restante...
efetuamos a mesma operação (recursividade) e somamos o prego ao que já temos.
Fazemos o mesmo até não existir mais pregos para contar, isto é, pegamos num e somamos
aos que temos, repetimos a mesma operação perguntando sempre entre as operações, "ainda há mais
pregos para contar?", caso haja, repetimos, caso contrário paramos.
A recursividade é uma ferramenta muita poderosa quando bem implementada, senão pode
ser muita perigosa. É preciso ter cuidado com as condições de parada, se faltar alguma condição de
parada ou alguma condição de parada está errada pode acontecer um ciclo infinito.
Paradigma recursivo:
• existência de casos simples, em que a resposta é determinada diretamente;
• ser possível uma decomposição recursiva de uma instância do problema, em instâncias
mais simples da mesma forma.
Numa função recursiva, são criadas várias ativações dela própria que desaparecem à medida
que a execução avança. Em cada momento apenas uma das ativações está ativa, estando as restantes
à espera que essa termine para continuarem.
Os dois paradigmas são equivalentes: dada uma função recursiva existe sempre uma iterativa
e vice-versa.
Exemplo de problema: Considerar uma população de coelhos que se reproduz segundo as seguintes
regras:
Cada par de coelhos produz um novo par por mês
Os coelhos são férteis a partir do segundo mês
Os coelhos não morrem
Supondo que nasce um par de coelhos em Janeiro, quantos pares de coelhos existem no fim do ano?
Algoritmo Determinar o número de pares em cada mês:
0 1 2 3 4 5 6 7 8 9 10 11 12
0 1 1 2 3 5 8 13 21 34 55 89 144
Generalizando, ao fim de n > 1 etapas temos: fn = fn - 1+fn - 2 e f0=0 e f1=1.
21
São dados três suportes (a, b e c) e n discos de tamanhos diferentes. Os discos estão empilhados num
dos suportes por ordem crescente de tamanhos. Pretende-se mover os discos para outro suporte de
modo que:
em cada passo exatamente um disco seja movido de um suporte para o outro
um disco não pode nunca estar por cima de um menor
o terceiro suporte pode ser usado como auxiliar
9. APONTADORES
É na memória RAM que são carregados os nossos programas e também onde são
armazenadas as variáveis que fazem parte dos programas. A memória RAM pode ser vista como um
enorme vetor de Bytes consecutivos, cada um ocupando uma posição bem determinada, que é
identificada por um número único que varia entre 0 e a totalidade de Bytes.
Para os programadores, é muito mais simples referenciar uma variável pelo seu nome do que
referenciá-la pela posição que essa variável ocupa em memória. O compilador associa a cada nome de
variável uma posição única em memória, capaz de suportar os dados do tipo dessa variável.
Sempre que num programa se faz referência a uma variável, na realidade é o endereço ou conjunto de
endereços que essa variável ocupa, que está sendo referenciado.
O apontador é um mecanismo particularmente flexível de manipulação de dados, pois permite
manipular diretamente dados contidos em endereços específicos de memória. Supondo que exista um
apontador denominado ptr, que como qualquer variável ocupa uma posição em memória. Como ptr é
um apontador, deverá conter o endereço de memória de outra variável (notar que o endereço de uma
variável não é mais do que o número da casa que ocupa em memória). A Figura 3 mostra este exemplo.
1) Fila de clientes de um banco, onde existem o primeiro e o último da fila, e uma “ordem” de
atendimento.
2) Pilha de processos de uma repartição a serem atendidos.
Os diferentes tipos de listas lineares, possuem características especiais com relação a forma
como são manipuladas.
Filas - Uma fila (queue) é uma lista linear onde as operações de inserção são efetuadas apenas no final
e as operações de retirada apenas no início, ou seja:
• A inserção de um novo elemento X o torna o último da fila;
• A retirada é sempre efetuada sobre o elemento E1.
X P T O R
Devido às características das operações da fila, o primeiro elemento a ser inserido será o
primeiro a ser retirado. Estruturas deste tipo são chamadas de FIFO (First In, First Out).
Pilhas - Uma pilha (stack) é uma lista linear onde tanto a operação de inserção, quanto a de retirada
são efetuadas no final, ou seja:
• A inserção de um novo elemento X o torna o último da pilha;
• A retirada é sempre efetuada sobre o elemento En.
R
23
Devido às características das operações da pilha, o primeiro elemento a ser inserido será o
último a ser retirado e o último a ser inserido será o primeiro a ser retirado. Estruturas deste tipo são
chamadas de LIFO (Last In, First Out).
Alternativas:
• Contigüidade física (com o uso de vetores)
• Encadeada (com o uso de apontadores)
Contigüidade
Fila:
1 2 3 4 5 6 7 8 9 10
R O T P X
início fim
Pilha:
6
topo 5 X
4 P
3 T
2 O
1 R
Encadeamento Simples
R O T P X
Exercícios:
2) Construa um procedimento que recebe uma lista encadeada (endereço inicial no apontador Lista) e
monta uma nova lista a partir dos dados desta, com os elementos em ordem inversa. Somente a lista
final deve estar alocada ao final da execução do procedimento.
3) Escreva um procedimento que recebe duas filas, que contém valores numéricos ordenados. O
procedimento deverá formar uma terceira fila, também ordenada, na qual estarão os valores
armazenados nas filas originais. Considere duas possibilidades: as filas implementadas sobre arranjos,
e as filas implementadas através de apontadores.