Apostila de COM146
Algoritmos e Estruturas de Dados I
Disciplina do 1o módulo do
Curso de Ciência da Computação
da Universidade Federal de Lavras
Olinda Nogueira Paes Cardoso1
Agosto de 2002
1
Professora Auxiliar do Departamento de Ciência da Computação da UFLA
1
ÍNDICE
1.
Introdução ......................................................................................................................................... 2
Necessidade do uso da lógica ...................................................................................................... 2
Conceitos Básicos......................................................................................................................... 2
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
1.1.
1.2.
1.3.
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.
A técnica mais importante no projeto da lógica de programas é chamada programação
estruturada, a qual consiste em uma metodologia de projeto que objetiva:
•
agilizar a codificação da escrita da programação;
•
facilitar a depuração da leitura da mesma;
•
permitir a verificação de possíveis falhas apresentadas pelos programas;
•
facilitar as alterações e atualizações dos programas.
1.2.
CONCEITOS BÁSICOS
O conceito central da programação em ciência da computação é o de algoritmo. Segundo
Wirth a programação estruturada é “a arte ou técnica de construir e formular algoritmos de uma forma
sistemática”.
Programas são “formulações concretas de algoritmos abstratos, baseados em representações
e estruturas específicas de dados”. Estruturas de dados são usadas no algoritmo para representar as
informações do problema a ser resolvido.
Na programação deve-se distinguir claramente dois aspectos:
Aspecto estático – a formulação de um algoritmo consiste em um texto contendo comandos
(instruções) que devem ser executados numa ordem prescrita.
Aspecto dinâmico – os efeitos que são causados pela execução do programa no tempo, dado um
conjunto de valores iniciais.
1.3.
CONCEITO DE ALGORITMO
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.
Exemplo de algoritmo para trocar uma lâmpada queimada:
“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”;
“retirar a lâmpada queimada”;
“colocar a lâmpada nova”;
“descer a escada”;
“testar se a lâmpada nova está funcionando”
O símbolo de seqüenciamento (;) tem duas funções: no texto a de separar um comando do
outro; e no evento a de indicar que os comandos separados devem ser executados na mesma
seqüência em que aparecem no texto.
O símbolo ; representa a mais simples estrutura de controle, a seqüência simples.
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.
O identificador é o elemento básico da linguagem, a sua sintaxe é definida pelo diagrama
apresentado na Figura 1:
letra
letra
dígito
Figura 1: Diagrama que representa um identificador
2.1.
DEFINIÇÃO DE VARIÁVEIS – TIPOS BÁSICOS
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
Toda variável deve ser declarada conforme a sintaxe apresentada na Figura 2.
A semântica de uma declaração de variáveis corresponde à criação de locais na memória,
rotulada com o nome do identificador (variável) e marcada com o tipo de valores que ela pode conter.
4
,
:
inteiro
;
identificador
real
caracter
lógico
Figura 2: Diagrama que representa a definição de uma variável.
2.2.
COMANDOS BÁSICOS
O comando de ATRIBUIÇÃO é utilizado para atribuir um valor a uma variável. Para isso usase 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 <=)
É importante observar que o resultado da expressão (do lado direito do comando de
atribuição) deve ser coerente com o tipo declarado para a variável (do lado esquerdo).
2.3.
BLOCOS E COMANDOS BÁSICOS DE CONTROLE
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
< declaração de variáveis >
< comandos >
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
Exemplo de uma estrutura condicional simples:
se < condição >
então
comando1;
comando2;
...
comandoN;
fim se;
Exemplo de uma estrutura condicional composta:
se < condição >
então
comando1;
comando2;
...
comandoN;
senão
comando1’;
comando2’;
...
comandoN’;
fim se;
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;
Uma estrutura de repetição é quando um conjunto de ações é executado repetidamente
enquanto uma determinada condição permanece válida (ou seja, quando o resultado de da expressão é
um valor lógico verdadeiro).
Exemplo de uma estrutura de repetição:
enquanto < condição > faça
comando1;
comando2;
...
comandoN;
fim enquanto;
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
Exemplo de um algoritmo que usa comandos de entrada e saída:
início
inteiro: A, B, SOMA;
leia (A, B);
SOMA ← A + B;
imprima (“A soma entre ”, A, “ e ”, B, “ é ”, SOMA);
fim;
Exercício: Escreva o algoritmo que realize a multiplicação de dois números inteiros, utilizando apenas o
operador da soma (+).
Regras práticas para a construção de algoritmos legíveis:
•
•
•
•
Procure incorporar comentários no algoritmo para descrever o significado das variáveis e
ações utilizadas. Para isso use chaves {}.
Escolha nomes de variáveis que sejam significativos, isto é, que traduzam o tipo de
informação a ser armazenada na variável. Por exemplo: NOTA, SOMA, MÉDIA, etc.
Grife as palavras-chave (escritas com letras minúsculas) do algoritmo, destacando as
estruturas de controle.
Procure alinhar os comandos de acordo com o nível a que pertencem, isto é, destaque a
estrutura na qual estão contidos.
7
Exercícios de Fixação: Construa algoritmos para solucionar os seguintes problemas.
1. Como saber se um número é divisível por outro.
2. Calcular a média final de um aluno que realizou 4 avaliações, sabendo que todas têm o mesmo
peso.
3. Dados 3 números, verificar qual deles é o menor.
2.4.
OUTRAS ESTRUTURAS DE REPETIÇÃO
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.
O comando abandone só tem sentido dentro de um comando de repetição (enquanto, repita e
para). Além disso, estará sempre associado ao teste de uma condição com comando se.
Sintaxe: abandone;
A semântica do comando é a seguinte: quando o abandone é encontrado, o próximo comando
a ser executado é o primeiro logo após o fim do comando de repetição mais interno onde este aparece.
Exemplo:
...
enquanto I > 0 faça
I ← I + 1;
imprima (I);
2
se I + 1 <= 150
então I ← I + 25;
senão abandone;
8
fim se;
fim enquanto;
2.5.
DEFINIÇÃO DE NOVOS TIPOS DE DADOS
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.
2.6.
ESTRUTURAS CONDICIONAIS ENCADEADAS
Existem casos em que é necessário se estabelecerem verificações de condições sucessivas,
onde uma determinada ação poderá ser executada se um conjunto anterior de condições for satisfeito.
Isto significa usar uma condição dentro de outra. Este tipo de estrutura pode possuir diversos níveis de
condição, sendo chamada de aninhamento ou encadeamento de estruturas condicionais. Exemplo:
se <condição 1> então
<comandos para condição 1 verdadeira>
senão
se <condição 2> então
<comandos para a condição 1 falsa e 2 verdadeira>
senão
<comandos para a condição 1 e 2 falsas >
9
fim se; {final do se mais interno}
fim se; {final do primeiro se (mais externo)}
Exercício: Escreva um algoritmo que efetue o cálculo de reajuste de salário de um funcionário.
Considere que o aumento será de 15% se o salário for até R$500,00, de 10% se for entre R$501,00 e
R$1.000,00 e de 5% se for maior que R$1.000,00.
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.
3.1.
ORDENAÇÃO DE VETORES
A preocupação mais importante a ser estabelecida em relação aos métodos de ordenação de
vetores corresponde ao uso econômico da memória disponível. Isto implica que a permutação de
elementos, responsável por levar o elemento à ordem desejada, deve ser efetuada in situ, e que,
portanto, são de menor interesse os métodos que efetuam o transporte físico dos elementos de um
vetor A para um vetor resultante B.
Restringindo-se a escolha dos métodos, dentre as inúmeras soluções possíveis, de acordo
com o critério de economia de memória, pode-se promover uma primeira classificação de acordo com a
eficiência do mesmo em relação à economia de tempo.
Uma boa medida de eficiência é obtida contando-se o número C de comparações necessárias
e o número M de movimentos (transposições) dos elementos. Estes números são funções do número n
de elementos a serem ordenados.
Os métodos de ordenação que ordenam os elementos in situ podem ser classificados em três
principais categorias:
• Ordenação por inserção
• Ordenação por seleção
• Ordenação por troca
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;
3.1.1.
ORDENAÇÃO POR INSERÇÃO
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
i=3
i=4
i=5
i=6
44
12
12
12
12
55
44
42
42
18
12
55
44
44
42
42
42
55
55
44
94
94
94
94
55
18
18
18
18
94
06
06
06
06
06
67
67
67
67
67
10
i=7
i=8
06
12
18
42
44
55
94
67
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;
3.1.2.
ORDENAÇÃO POR SELEÇÃO
Este método é baseado no seguinte princípio:
• Selecionar o elemento que apresenta o menor valor
• Trocá-lo com o primeiro elemento da seqüência A[1]
• Repetir estas operações, envolvendo agora os N–1 elementos restantes, depois os N–2
elementos, ..., até restar um só elemento, o maior deles.
Valores iniciais
44
06
06
06
06
06
06
06
55
55
12
12
12
12
12
12
12
12
55
18
18
18
18
18
42
42
42
42
42
42
42
42
94
94
94
94
94
44
44
44
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;
18
18
18
55
55
55
55
55
06
44
44
44
44
94
94
67
67
67
67
67
67
67
67
94
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.
4.1.
MATRIZES BIDIMENSIONAIS
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.
Exemplo: Definição de uma matriz com 8 linhas e 5 colunas.
tipo mat = matriz [1..8, 1..5] inteiro;
mat: TABELA;
TABELA
1
2
3
4
5
6
7
8
1
2
3
4
5
Exemplo de algoritmo utilizando matriz com duas dimensões:
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
5.1.
REGISTRO COM VETOR
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);
imprima (“Digite a turma do aluno”);
leia (ALUNO.TURMA);
imprima (“Digite o período do aluno”);
leia (ALUNO.PERIODO);
imprima (“Digite as notas do aluno”);
para I de 1 até 6 faça
leia (ALUNO.NOTAS[I]);
fim para;
fim
5.2.
CONJUNTOS DE REGISTROS
No conjunto de registros são armazenados dados de várias ocorrências de um determinado
tipo de registro. Por exemplo, para armazenar os dados de diversos alunos:
início
inteiro: I, J, N;
tipo vet = vetor [1..6] real;
tipo reg = registro
caracter: MAT
caracter: NOME
inteiro: IDADE
caracter: TURMA
inteiro: PERIODO
vet: NOTAS
fim registro;
tipo aluno = conjunto [1..N] reg;
aluno: ALUNOS;
imprima (“Digite o número de alunos”);
leia (N);
para I de 1 até N faça
imprima (“Digite a matrícula do aluno”);
leia (ALUNOS[I].MAT);
imprima (“Digite o nome do aluno”);
leia (ALUNOS[I].NOME);
imprima (“Digite a idade do aluno”);
leia (ALUNOS[I].IDADE);
imprima (“Digite a turma do aluno”);
leia (ALUNOS[I].TURMA);
imprima (“Digite o período do aluno”);
leia (ALUNOS[I].PERIODO);
imprima (“Digite as notas do aluno”);
para J de 1 até 6 faça
leia (ALUNOS[I].NOTAS[J]);
fim para;
fim para;
fim
6. ARQUIVOS
Até o momento, todas as estruturas de dados estudadas ficaram armazenadas no ambiente do
algoritmo, ou seja, tinham duração apenas enquanto o algoritmo estava sendo executado. O arquivo é
uma alternativa para estrutura de dados que pode ser fisicamente alocado em outro meio de
armazenamento, em disco, por exemplo.
14
Um registro é a parte lógica de uma estrutura de dados, enquanto que um arquivo,
constituído por um conjunto de um ou mais registros, é a parte física, e pode ser chamado de registro
físico.
6.1.
ORGANIZAÇÃO DE 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;
Para se desfazer a associação entre o modelo e o arquivo físico, usa-se o comando de
fechamento:
feche NOME-DO-ARQUIVO;
Os formatos para leitura e escrita de um arquivo são dependentes do seu tipo de organização.
6.1.1.
ORGANIZAÇÃO SEQÜENCIAL
A principal característica da organização seqüencial é a de que os registros são armazenados
contiguamente, isto é, um após o outro na ordem em que foram inseridos. A acesso aos registros do
arquivo, tanto na leitura quanto na escrita, são feitos seqüencialmente, ou seja, a leitura de um registro
só é possível após a leitura de todos os registros anteriores e a escrita de um registro só é feita após o
último registro.
15
O comando de leitura de um registro em um arquivo seqüencial é:
leia NOME-DO-ARQUIVO . NOME-DO-REGISTRO;
E o comando de escrita similar é:
escreva NOME-DO-ARQUIVO . NOME-DO-REGISTRO;
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 1: Supondo-se a existência de um arquivo A composto por nomes, salários e número de
horas trabalhadas por mês dos funcionários de uma empresa, faça um algoritmo que crie um novo
arquivo B, com a mesma estrutura, porém contendo apenas os dados referentes aos funcionários que
trabalharam mais de 400 horas / mês.
Exercício 2: Crie um algoritmo para entrar com dados, a partir do teclado, de um novo funcionário no
arquivo A.
6.2.
ORGANIZAÇÃO DIRETA
A principal característica da organização direta é a facilidade de acesso a um registro
desejado, pois, ao contrário da organização seqüencial, para acessar um determinado registro não é
preciso percorrer todos os anteriores a ele, o acesso é feito diretamente.
Este acesso direto é possível porque a posição do registro no espaço físico do arquivo é
univocamente determinada a partir de um dos campos do registro, escolhido no momento de criação do
arquivo direto como sua chave.
Exemplo: Suponha-se um arquivo seqüencial contendo dados dos alunos, como na tabela abaixo:
MATRICULA
NOME
TURMA
PERIODO
9800012
Maria Araújo
A
3
9900001
Joaquim Silva
A
2
9800002
Carlos Menezes
B
3
9900010
Fátima Andrade
C
1
9900025
Ana Lúcia Dias
B
1
9800005
Marcelo Costa
C
3
9900003
Flávio Martins
C
2
9800040
Luiz Carvalho
A
2
Um algoritmo para encontrar os dados do aluno e imprimir seu nome, cuja matrícula seja
9900003, seria o seguinte:
início
tipo regaluno = registro
caracter: MATRICULA
caracter: NOME
caracter: TURMA
inteiro: PERIODO
fim registro;
regaluno: DADOS;
arquivo seqüencial de DADOS: ALUNOS;
abra ALUNOS leitura;
repita
leia ALUNOS.DADOS;
se DADOS.MATRICULA = “9900003” então
imprima (DADOS.NOME);
abandone;
fim se;
até ALUNOS.FDA;
feche ALUNOS;
fim.
16
Se a organização for direta, a disposição dos registros no arquivo não será
necessariamente a apresentada no arquivo seqüencial.
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:
{função que associa chave ao registro}
MATRICULA
9800002
9800005
9800012
9800040
9900001
9900003
9900010
9900025
NOME
Maria Araújo
Joaquim Silva
Carlos Menezes
Fátima Andrade
Ana Lúcia Dias
Marcelo Costa
Flávio Martins
Luiz Carvalho
TURMA
A
A
B
C
B
C
C
A
PERIODO
3
2
3
1
1
3
2
2
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;
O algoritmo desenvolvido anteriormente para encontrar os dados do aluno e imprimir seu
nome, cuja matrícula seja 9900003, seria:
início
tipo regaluno = registro
caracter: MATRICULA
caracter: NOME
caracter: TURMA
inteiro: PERIODO
fim registro;
regaluno: DADOS;
arquivo direto de DADOS chave MATRICULA: ALUNOS;
abra ALUNOS leitura;
leia item [9900003]ALUNOS.DADOS;
se ALUNOS.INV {erro se a chave não for encontrada}
então
imprima (“Aluno não existe”);
senão
imprima (DADOS.NOME);
fim se;
feche ALUNOS;
fim.
17
Exercícios:
1. Escreva um algoritmo que abra um arquivo VENDAS contendo os seguintes campos: código da peça,
quantidade vendida, valor unitário, cliente e data. E crie dois outros arquivos da seguinte forma:
•
•
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
No fim da década de 60, alguns problemas no desenvolvimento de sistemas de programação
levaram os países desenvolvidos a um evento chamado “crise de software”. Os custos das atividades de
programação mostravam a cada ano uma clara tendência a se elevarem muito em relação aos custos
dos equipamentos, e isto era devido ao avanço tecnológico na fabricação dos equipamentos de
computação e a lenta evolução de técnicas aplicadas ao desenvolvimento de software.
A ausência de uma metodologia para a construção de programas conduzia a programas
geralmente cheios de erros e com altos custos de desenvolvimento que, conseqüentemente, exigiam
custos elevados para a sua correção e manutenção futuras. A programação estruturada foi o resultado
de uma série de estudos e propostas de metodologias para desenvolvimento de software. Uma das
técnicas aplicadas na programação estruturada, a modularização de programas é uma ferramenta para
a elaboração de programas visando, os aspectos de confiabilidade, legibilidade, manutenibilidade e
flexibilidade, dentre outros.
A modularização é um processo que aborda os aspectos da decomposição de algoritmos em
módulos. Módulo é um grupo de comandos, constituindo um trecho do algoritmo, com uma função bem
definida e o mais independente possível em relação ao resto do algoritmo.
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.
Onde “Determine o salário” pode ser refinado como:
Calcule as vantagens
Calcule as deduções
SALARIOLIQ ← VANTAGENS – DEDUÇÕES
No refinamento anterior não houve preocupação de como o processo de cálculo das
vantagens e deduções seria efetuado. Essas ações constituem funções bem definidas e que serão
executadas por módulos específicos, neste caso, o algoritmo anterior ficaria:
início
Leia os dados do empregado
Ative o módulo “Cálculo das vantagens”
Ative o módulo “Cálculo das deduções”
SALARIOLIQ ← VANTAGENS – DEDUÇÕES
Escreva o salário
fim.
Exemplo da descrição estrutural da modularização:
Módulo Principal
Módulo Vantagens
Módulo Deduções
18
A maneira mais intuitiva de proceder a modularização de problemas é feita definindo um
módulo principal de controle e módulos específicos para as funções do algoritmo. Recomenda-se que os
módulos de um programa tenham um tamanho limitado, pois módulos muito grandes são difíceis de ser
compreendidos e, em geral, são multifuncionais.
As linguagens de programação dispõem de recursos que facilitam a construção e manipulação
de módulos, permitindo não só a modularização dos comandos do programa, como também dos dados
utilizados.
Cada módulo pode definir as próprias estruturas de dados, suficientes e necessárias apenas
para atingir o objetivo final do módulo. Todo módulo é constituído por uma seqüência de comandos que
operam sobre um conjunto de objetos, que podem ser globais ou locais.
Objetos globais são entidades que podem ser usadas em módulos internos a outro módulo
do algoritmo onde foram declaradas.
Objetos locais são entidades que só podem ser usadas no módulo do algoritmo onde foram
declaradas. Estes objetos não possuem nenhum significado fora deste módulo.
São exemplos de objetos globais ou locais: variáveis, arquivos, outros módulos, etc.
A comunicação entre módulos deverá ser feita através de vínculos, utilizando-se objetos
globais ou transferência de parâmetros.
7.1.
BENEFÍCIOS DA MODULARIZAÇÃO
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.
7.2.
FERRAMENTAS PARA MODULARIZAÇÃO
Sub-rotinas e funções são módulos que servem aos objetivos:
• Evitar que em certa seqüência de comandos necessária em vários locais de um algoritmo
tenha que ser escrita repetidamente nesses locais;
• Dividir e estruturar um algoritmo em partes fechadas e logicamente coerentes;
• Aumentar a legibilidade de um algoritmo.
Sub-rotinas e funções são módulos hierarquicamente subordinados a um algoritmo,
comumente chamado de módulo principal. Da mesma forma uma sub-rotina ou uma função pode conter
outras sub-rotinas e funções aninhadas.
A sub-rotina e a função são criadas através das suas declarações em um algoritmo e para
serem executadas, necessitam de ativação por um comando de chamada. A declaração de uma subrotina ou função é constituída de um cabeçalho, que a identifica e contém seu nome e uma lista de
parâmetros formais, e de um corpo que contém declarações locais e os comandos.
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);
As funções têm a característica de retornar ao algoritmo que as chamou um valor associado
ao nome da função.
19
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);
Ao terminar a execução dos comandos de uma sub-rotina ou função, o fluxo de controle
retorna ao comando seguinte àquele que provocou a chamada.
7.3.
MODOS DE TRANSFERÊNCIA DE PARÂMETROS
Os parâmetros de uma sub-rotina ou função classificam-se em:
de entrada – são aqueles que têm seus valores estabelecidos fora da sub-rotina ou função e não
podem ser modificados dentro dela.
de saída – são aqueles que têm seus valores estabelecidos dentro da sub-rotina ou função.
de entrada-saída – são aqueles que têm seus valores estabelecidos fora da sub-rotina ou função, mas
podem ter seus valores alterados dentro dela.
A vinculação entre módulos pode ser feita através da transferência ou passagem de
parâmetros, que associam parâmetros atuais com parâmetros formais. Dentre os modos de
transferência de parâmetros, pode-se destacar: a passagem por valor, a passagem por resultado e a
passagem por referência.
Na passagem de parâmetros por valor, as alterações feitas nos parâmetros formais, dentro da
sub-rotina ou função, não se refletem nos parâmetros atuais. O valor do parâmetro atual é copiado no
parâmetro formal, na chamada da sub-rotina ou função. Assim, quando a passagem é por valor significa
que o parâmetro é de entrada.
Na passagem de parâmetros por resultado, as alterações feitas nos parâmetros formais, na
sub-rotina ou função, refletem-se nos parâmetros atuais. O valor do parâmetro formal é copiado no
parâmetro atual, ao retornar da sub-rotina ou função. Assim, quando a passagem é por resultado
significa que o parâmetro é de saída.
Na passagem de parâmetros por referência, a toda alteração feita num parâmetro formal
corresponde a mesma alteração feita no seu parâmetro atual associado. Neste caso, quando a
passagem é por valor significa que o parâmetro é de entrada-saída.
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.
8.1.
EXEMPLO DE PROBLEMA RECURSIVO
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.
8.2.
RECURSÃO × ITERAÇÃO
Paradigma iterativo: uma seqüência de instruções é executada de uma forma repetitiva,
controlada por uma dada condição (ciclo iterativo).
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.
8.3.
EXEMPLOS MAIS FAMOSOS DE PROBLEMAS RECURSIVOS
Fatorial: Cálculo de n! = n x (n - 1) x...x 1
Seqüência de Fibonacci: 0 1 1 2 3 5 8 13 21 34 55 89 144
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
O Puzzle das Torres de Hanói: (inventado por Eduard Lucas (1880))
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.
Figura 3: Exemplo de apontador.
22
10. LISTAS LINEARES
Uma lista linear é uma estrutura dinâmica caracterizada por uma seqüência ordenada de
elementos, no sentido da sua posição relativa: E1, E2, ..., En, onde:
• Existem n elementos na seqüência;
• E1 é o primeiro elemento da seqüência;
• En é o último elemento da seqüência;
• Para todo i, j entre 1 e n, se i < j, então o elemento Ei antecede o elemento Ej;
• Caso i = j – 1, Ei é o antecessor de Ej e Ej é o sucessor de Ei.
Exemplos de listas lineares:
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.
10.1. OPERAÇÕES EM LISTAS LINEARES
Criar – uma estrutura dinâmica será criada durante a execução do programa;
Destruir – depois de ser utilizada, a estrutura deve ser destruída;
Percorrer – todos os elementos da lista podem ser utilizados, sendo que para isso a lista tem que ser
percorrida;
Buscar – um determinado elemento da lista pode ser identificado ou por sua posição, ou por seu
conteúdo;
Inserir – um novo elemento é colocado na lista numa determinada posição e n aumenta em 1;
Remover – um elemento é retirado da lista numa determinada posição e n diminui em 1.
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.
P
T
O
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).
10.2. IMPLEMENTAÇÃO DE LISTAS LINEARES
Alternativas:
• Contigüidade física (com o uso de vetores)
• Encadeada (com o uso de apontadores)
Contigüidade
Fila:
1
2
3
4
R
5
O
6
T
7
P
8
X
início
9
10
fim
Pilha:
topo
6
5
4
3
2
1
X
P
T
O
R
Encadeamento Simples
R
O
T
P
X
Exercícios:
1) Considere um conjunto de informações relativas a alunos, constituído de nome, número de matrícula
e data de nascimento. Organize estas informações em uma lista encadeada, ordenada pelo nome do
aluno. Escreva funções que efetuem as seguintes ações:
• imprimir os nomes e números de matrícula dos alunos que nasceram após uma determinada
data (passada como parâmetro);
• procurar as informações relativas a um determinado aluno, cujo número de matrícula é passado
como parâmetro;
• incluir um novo aluno na lista, respeitando a ordenação.
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.
Download

Apostila de COM146 Algoritmos e Estruturas de Dados I