estrutura, organização e

Propaganda
ESCOLA SECUNDÁRIA DE VOUZELA
Curso Tecnológico de Informática
12º Ano
Apontamentos
Prof. José Alberto Pereira
1ª Versão
1999/2000
NOTA INTRODUTÓRIA
O objectivo destes apontamentos não é substituir as aulas, como tal, a exposição
de qualquer matéria não é realizada de modo detalhado. Será necessário consultar
entre outra bibliografia, a bibliografia geral da disciplina e outros manuais de
aplicação. O aluno também deve procurar uma actualização permanente, criando o
hábito de consultar publicações técnicas, investigar novos conceitos e discutir sobre
eles.
A disciplina tem áreas perfeitamente teóricas e rubricas eminentemente práticas.
Assim, uma a duas horas semanais serão dadas para desenvolvimento dos pontos
teóricos ao longo do ano. As restantes horas serão para exploração das unidades
práticas. Por fim no terceiro período haverá uma unidade final denominada projecto
e um exame a nível nacional cuja realização não é em laboratório.
Para além dos testes a avaliação desta disciplina é centrada nos trabalhos
práticos desenvolvidos pelo aluno, no acompanhamento do seu progresso, da sua
capacidade de trabalho em equipa e na observação da sua criatividade. A avaliação
desta disciplina é complementada pela observação da evolução do desempenho
técnico do aluno na sala de aula, no uso de software de base e ferramentas
adicionais, capacidade de ultrapassar dificuldades e solucionar problemas, bem
como facilidade de trabalho em equipa.
O professor: José Alberto Pereira
ÍNDICE
I ALGORITMOS
1
1.
PSEUDOCÓDIGO
1
2.
VARIÁVEIS
2
3.
OPERADORES
3
4.
INSTRUÇÕES
4
4.1.
4.2.
5.
INSTRUÇÕES SIMPLES
Instruções de escrita
4
Instruções de leitura
4
Instruções de atribuição
5
INSTRUÇÕES ESTRUTURADAS OU ESTRUTURAS DE CONTROLO
6
Instruções compostas
6
Estrutura de decisão condicional - "Se"
7
Estrutura de escolha múltipla - "Caso"
8
Estrutura de repetição ou ciclos
9
Estrutura de repetição ou ciclos - "Repita para"
9
Estrutura de repetição ou ciclos - "Repita enquanto"
11
Estrutura de repetição ou ciclos - "Repetir ... até"
11
SUBALGORITMOS
5.1.
4
EXEMPLOS
12
14
Função MÉDIA
14
Exemplo de utilização da função MÉDIA
14
Procedimento DIVIDE
15
Exemplo de utilização do procedimento DIVIDE
15
II ESTRUTURAS DE DADOS
16
1.
ESTRUTURA DE DADOS
16
2.
ESTRUTURA DE DADOS SIMPLES
16
2.1.
VALORES LÓGICOS
17
2.2.
INTEIROS
17
2.3.
REAIS
18
ESEV(Prof.Serv.) - Prof. José Alberto Pereira
i
Índice
3.
2.4.
ALFANUMÉRICOS
20
2.5.
PONTEIROS
21
ESTRUTURA DE DADOS COMPLEXAS
3.1.
VECTORES
21
3.2.
MATRIZES
23
3.3.
REGISTOS
24
3.4.
LISTAS
25
3.5.
PILHAS
25
Representação de pilhas em pascal
3.6.
FILAS
Representação de filas em pascal
3.7.
3.8.
3.9.
4.
21
LISTAS LIGADAS
26
28
29
38
Inserção de nós numa lista ligada
40
Remoção de nós numa lista ligada
41
Inserção de um elemento depois de um determinado nó
43
Implementação de uma pilha por meio de uma lista
44
Implementação de uma fila por meio de uma lista
45
FICHEIROS
46
Ficheiros do tipo texto
47
Ficheiros do tipo binário ou definidos pelo programador
47
Operações com ficheiros
48
Instruções em pseudocódigo para manipulação de ficheiros
49
Suportes físicos de ficheiros
50
ÁRVORES BINÁRIAS
51
Conceitos Básicos
51
Estrutura de uma árvore binária
52
Operações sobre uma árvore binária
52
Aplicações de árvores binárias
53
Travessias de uma árvores binárias
55
Algumas funções recursivas
57
MÉTODOS DE ORDENAÇÃO E PESQUISA
58
4.1.
PESQUISA LINEAR NUM VECTOR NÃO ORDENADO
58
1ª Versão
58
2º Versão
59
4.2.
PESQUISA LINEAR NUM VECTOR ORDENADA
59
4.3.
PESQUISA BINÁRIA
60
4.4.
FUSÃO SIMPLES
61
ESEV(Prof.Serv.) - Prof. José Alberto Pereira
ii
Índice
4.5.
ORDENAÇÃO POR INSERÇÃO SIMPLES
62
4.6.
ORDENAÇÃO POR SELECÇÃO SIMPLES
63
4.7.
ORDENAÇÃO POR TROCA SIMPLES
64
ESEV(Prof.Serv.) - Prof. José Alberto Pereira
iii
I
ALGORITMOS
1.
PSEUDOCÓDIGO
Por pseudocódigo entende-se um código de escrita em que se utilizam
representações simbólicas para indicar as instruções do algoritmo. Essas
representações simbólicas são, usualmente, um misto de palavras da nossa
linguagem natural com termos e notações típicas de uma linguagem de
programação.
O uso da escrita em pseudocódigo presta-se a uma aproximação sucessiva à
versão do algoritmo na linguagem utilizada, ou seja, pode-se ir progredindo por
fases, revendo o pseudocódigo e substituindo-o progressivamente por terminologia
própria da linguagem de programação.
Não é demais relembrar que, depois de efectuar o algoritmo, deve-se verificar se
está escrito sem falhas do ponto de vista dos objectivos que se pretendiam alcançar,
por imprecisões, deficiente formulação algorítmica, vícios de raciocínio, etc.
Sintaxe de um algoritmo em pseudocódigo:
Algoritmo <Título>
<Descrição>
<Passo>. [<Comentário>]
<Instruções>
(...)
ESEV(Prof.Serv.) - Prof. José Alberto Pereira
1
Unidade I
Algoritmos
Exemplo de um algoritmo em pseudocódigo:
Algoritmo PERCENTAGEM
Este algoritmo, para um vector de 15 notas de um teste, determina a percentagem dos
alunos com nota superior à média.
1. [Inicializar variáveis]
SOMA ! 0
CONT ! 0
2. [Ler as 15 notas e calcular a sua soma]
Repita para K=1,2,...,15
Leia(NOTA[K])
SOMA ! SOMA + NOTA[K]
3. [Calcular a média]
MÉDIA ! SOMA / 15
4. [Calcular o número de alunos com nota superior à média]
Repita para K=1,2,...,15
Se NOTA[K] > MÉDIA
Então CONT ! CONT + 1
5. [Calcular e imprimir a percentagem]
PERC ! (100 * CONT) / 15
Escreva('A percentagem de alunos com nota superior à média é ',PERC,'%')
6. [Terminar]
Saída
2.
VARIÁVEIS
As variáveis podem assumir:
•
um carácter global, quando são declaradas para uso em todo o algoritmo;
•
um carácter local, quando são declaradas apenas para uso dentro do
subalgoritmo.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
2
Unidade I
3.
Algoritmos
OPERADORES
Operadores em pseudocódigo:
OPERADORES ARITMÉTICOS
Designação
Operador
Adição
+
Subtracção
Multiplicação
*
Divisão
/
Os operadores aritméticos utilizam-se normalmente com dois operandos. No
entanto, os operadores + e - também podem ser usados com um só operando, caso
em que significam: o sinal positivo ou o sinal negativo atribuído ao operando.
OPERADORES DE COMPARAÇÃO
Designação
Operador
igual a
=
diferente de
≠
Maior que
>
menor que
<
Maior ou igual a
≥
menor ou igual a
≤
Os operandos empregues com os operadores de comparação devem ser de
tipos compatíveis entre si. Os resultados que devolvem são sempre do tipo valor
lógico, ou seja, verdadeiro ou falso. Estes operadores podem ser usados com
quaisquer tipos de ordinais, com reais, alfanuméricos e com ponteiros.
OPERADORES DE VALORE LÓGICO
Designação
Operador
Negação
não
Conjunção
e
Disjunção
ou
Os operadores de valores lógicos operam normalmente com operandos do tipo
valor lógico e devolvem resultados desse mesmo tipo. Também são utilizados com
muita frequência, tal como os operadores relacionais, em estruturas de decisão e no
controlo de ciclos de repetição.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
3
Unidade I
4.
Algoritmos
INSTRUÇÕES
Tipicamente, um algoritmo consiste numa série de instruções escritas numa
determinada linguagem, segundo determinada ordem.
As instruções são frases, por assim dizer, que enunciam ou indicam as acções
ou operações que se pretende realizar com o algoritmo.
As instruções num algoritmo podem ser:
•
Instruções simples;
•
Instruções estruturadas ou estruturas de controlo.
Umas e outras são compostas por comandos. Os comandos são, normalmente,
palavras, abreviaturas ou conjuntos de caracteres que sugerem a acção que é
desempenhada; por exemplo: Saída; Se; Então; Repita; Escreva; Leia; etc.
4.1.
Instruções Simples
Instruções de escrita
As instruções de escrita são aquelas que servem para enviar dados (mensagens,
valores de variáveis, etc.) para um dispositivo de saída ou periférico de "output".
Como se sabe, o dispositivo de saída mais comum no trabalho com um
computador é o monitor de vídeo ou ecrã; no entanto, o envio de dados para a
impressora ou para um ficheiro em disco ou disquete também são tarefas normais e,
por vezes, mesmo imprescindíveis.
Exemplos das instruções de escrita, mais usuais, em pseudocódigo:
[Imprimir a mensagem]
Escreva('Não existe quarto vago do tipo escolhido.')
[Imprimir a percentagem]
Escreva('A percentagem de alunos com nota superior à media é ', PERC, '%')
Instruções de leitura
Tal como a saída de dados do computador para um periférico é essencial,
também a entrada de dados o é.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
4
Unidade I
Algoritmos
Os principais dispositivos que permitem a leitura, entrada ou "input" de dados
num sistema informático são, como se sabe, em primeiro lugar, o teclado, em
segundo, os dispositivos de armazenamento secundário, ou seja os discos e
disquetes, e ainda mais alguns outros, como o rato, etc.
Exemplo de uma instrução de escrita em pseudocódigo:
[Ler a remuneração mensal]
Leia(REMUNERAÇÃO)
[Ler as variáveis X e Y ]
Leia(X, Y)
Instruções de atribuição
Muitos dos dados com que se opera num algoritmo são variáveis. As variáveis e
constantes num algoritmo são designadas por meio de identificadores. Um
identificador é um nome normalmente atribuído pelo algoritmo ou utilizador a um
elemento com que se pretende trabalhar dentro de um algoritmo.
Exemplos,
apresentados
numa
tabela
com
possíveis
Nome do cliente
Morada dos clientes
Data em que foi feita a encomenda
NOME
MORADA
DATA
entidades
e
os
correspondentes identificadores das variáveis:
Por convenção, vamos escrever o nome dos identificadores em letras
maiúsculas, para maior legibilidade do algoritmo.
Quando se trata de variáveis, um identificador vai traduzir-se em termos de
compilação, num endereço de memória, o qual corresponde a um determinado
espaço nessa mesma memória, onde se vai armazenar um dado - o valor que a dita
variável assume em cada momento. Isto leva-nos a considerar, em programação,
que um identificador é um endereço simbólico, pois que se preferimos, um candidato
a endereço de memória.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
5
Unidade I
Algoritmos
Exemplos de instruções de atribuição, em pseudocódigo:
[Inicializar variáveis]
SOMA ! 0
[Calcular a Média e incrementar Valor]
MÉDIA ! SOMA / 15
VALOR ! VALOR + 1
4.2.
Instruções Estruturadas ou Estruturas de Controlo
Nas estruturas de um algoritmo é, frequentemente, necessário:
•
jogar com determinadas condições, para decidir se se deve executar uma ou
outra acção;
•
repetir uma série de instruções, um determinado número de vezes ou
enquanto se verificar uma certa condição;
•
noutros casos ainda, temos um conjunto de instruções que se repete em
diversos pontos do programa, tornando-se então útil passar a tratá-lo como
um subalgoritmo.
Instruções compostas
Uma instrução composta não é mais que um conjunto de instruções englobadas
entre dois delimitadores - um início e um fim da referida instrução ("Início ... Fim") que, assim deve ser considerada um bloco.
A Sintaxe, em pseudocódigo, é a seguinte:
<Instrução 1>
<Instrução 2>
Início
<Instrução 2.1>
...
<Instrução 2.n>
Fim
...
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
6
Unidade I
Algoritmos
Estrutura de decisão condicional - "Se"
A estrutura de decisão condicional mais difundida e utilizada é a que se baseia
na instrução "Se".
As estruturas "Se" podem ser "encaixadas" umas dentro das outras.
A Sintaxe desta estrutura, em pseudocódigo, é a seguinte:
Se <Condição>
Então <Instrução se condição verdadeira>
[Senão <Instrução se condição falsa>]
•
<Condição> - a condição de controlo é normalmente uma expressão do tipo
lógico, isto é, que pode assumir apenas um entre dois valores possíveis:
verdadeiro ou falso;
•
<Instrução ...> - obviamente, será uma determinada acção que se pretende
efectuar. Esta instrução tanto pode ser uma instrução simples, como uma
instrução composta, ou seja, um conjunto de instruções;
•
o significado dos parêntesis rectos [...] indica que se trata de uma parte
opcional. Assim, o comando Senão e a sua respectiva instrução é opcional, ou
seja, só é utilizável quando se desejar ou for necessário.
Se tivermos um conjunto de instruções (instruções compostas) para considerar
dentro da estrutura podemos inseri-la entre delimitadores "Início ... Fim".
Exemplos de estruturas de decisão condicional, em pseudocódigo:
[Exemplo 1]
Se X = 1 e K > VALOR
Então Se K < MENOR
Então M ! VALOR
[Exemplo 2a]
Se ANDAR = 0
Então QUARTO_VAGO ! 0
K!0
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
7
Unidade I
Algoritmos
[Exemplo 2b]
Se ANDAR = 0
Então Início
QUARTO_VAGO ! 0
K!0
Fim
Estrutura de escolha múltipla - "Caso"
Numa estrutura "Caso" há uma variável, cujos valores que possa assumir vão
ser utilizados no controlo das alternativas ou acções a escolher - a esta variável é
costume chamar-se selector.
A Sintaxe desta estrutura, em pseudocódigo, é a seguinte:
Caso <Variável/Selector>
<Valor 1> : <Instrução 1>
<Valor 2> : <Instrução 2>
(...)
<Valor n> : <Instrução n>
[Senão : <Instrução>]
Fim (Caso).
Quando se indica <Valor 1>, <Valor 2>, etc., estamos a referir aos valores
possíveis que a variável de controlo poderá assumir. Pode ser apenas um valor ou
um conjunto de valores.
Quando de indica <Instrução 1>, < Instrução 2>, etc., pode tratar-se de uma só
instrução ou de uma instrução composta, portanto um conjunto de acção a realizar.
Em algumas implementações existe também uma cláusula "Senão" que se
destina a complementar o leque de alternativas, no caso de haver ainda mais
hipóteses não explicitadas, e em que pode haver ainda uma outra acção ou conjunto
de acções a indicar.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
8
Unidade I
Algoritmos
Estrutura de repetição ou ciclos
Frequentemente é necessários repetir uma certa instrução ou conjunto de
instruções um determinado número de vezes ou manter um ciclo ("Loop") de
repetições um número indeterminado de vezes, enquanto se verificar certa condição.
Essa repetição, na maior parte das vezes, não tem que ser uma repetição exacta
das mesmas operações, pois pode haver pelo meio certos dados (variáveis) ou
parâmetros que se vão alterando à medida que o ciclo vai decorrendo.
Exemplo de estruturas de repetição:
•
Repita para ...
•
Repita enquanto ...
•
Repetir ... até
Enquanto numa estrutura do tipo "Repita para..." o número de vezes que vai
ocorrer a repetição do ciclo é determinada à partida por uma variável de controlo que
é incrementada ou decrementada à medida que o ciclo decorre. Na estrutura "Repita
Enquanto..." e "Repetir...até" o ciclo decorrerá um número indeterminado de vezes,
dependendo da verificação ou não da condição de controlo - o que depende dos
acontecimentos no decurso do próprio ciclo.
Estrutura de repetição ou ciclos - "Repita para"
Esta estrutura de repetição é controlada por uma variável - Variável de controlo que parte de um determinado valor e incrementa ou decrementa até um outro
determinado valor.
A Sintaxe, em pseudocódigo, é a seguinte:
Repita para <Variável>=<Valores>
<Instrução>
Ou ainda:
Repita até ao passo <Passo> para <Variável>=<Valores>
<Instrução>
...
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
9
Unidade I
Algoritmos
As expressões <Valores> podem ser valores numéricos directos, valores de
ouras variáveis ou expressões, desde que esses valores sejam números inteiros.
À variável que controla o ciclo, também é costume chamar "Índice" ou variável de
iteração, na medida em que vai assumindo valores sucessivos.
Os valores da variável de controlo podem variar num sentido crescente ou
decrescente.
Se tivermos um conjunto de instruções (instruções compostas) para considerar
dentro da estrutura podemos inseri-la entre delimitadores "Início ... Fim".
Outra Sintaxe (não usada em exame):
Para <Variável> desde <Valor Inicial> até <Valor Final>, incremento <Incremento>
<Instrução 1>
<Instrução 2>
...
<Instrução n>
Fim (Para)
Exemplos, em pseudocódigo:
[Ler 10 elementos de um vector]
Repita para K=1,2,...,15
Leia (VEC[K])
[Ler as 15 notas e calcular a sua soma]
Repita para K=1,2,...,15
Leia (NOTA[K])
SOMA ! SOMA + NOTA[K]
[Inicializar a matriz TOTAIS]
Repita para I=1,2,...,10
Repita para J=1,2,...5
TOTAIS[I, J] ! 0
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
10
Unidade I
Algoritmos
Outro exemplo:
1. [Percorre todos os dias da semana e inicializar a quantidade mínima]
Repita até ao passo 4 para J=1,2,...,7
QUANT_MIN ! SEMANAS[J]
2. [Procura o dia da semana mais eficiente]
(...)
Estrutura de repetição ou ciclos - "Repita enquanto"
O ciclo ou estrutura de repetição começa com a verificação de uma expressão
ou condição, digamos, a "condição de controlo".
Se a condição for verdadeira, a instrução ou conjunto de instruções, será
executado um número vezes indeterminado à partida, dependendo de a condição de
controlo se manter verdadeira ou passar a falsa.
A Sintaxe, em pseudocódigo, é a seguinte:
Repita enquanto <Condição>
<Instrução>
Se tivermos um conjunto de instruções (instruções compostas) para considerar
dentro da estrutura podemos inseri-la entre delimitadores "Início ... Fim".
Exemplo, em pseudocódigo:
[Pesquisa um quarto vago]
J!1
QUARTO_VAGO ! 0
Repita enquanto J ≤ 10 e QUARTO_VAGO=0
Se QUARTOS[J] = 0
Então QUARTO_VAGO ! J
J!J+1
Estrutura de repetição ou ciclos - "Repetir ... até"
Como a condição de controlo de repetição só é avaliada no final do ciclo, a
instrução ou instruções será(ão) executada(s) sempre pelo menos uma vez.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
11
Unidade I
Algoritmos
O ciclo será interrompido quando a condição de controlo for verdadeira.
Sintaxe em pseudocódigo:
Repetir
<Instrução 1>
<Instrução 2>
...
<Instrução n>
até <Condição>
5.
SUBALGORITMOS
A decomposição de um problema é um aspecto muito importante na resolução
de qualquer problema. Os subalgoritmos são uma ferramenta importante que
possibilitam a divisão de um complexo algoritmo num determinado número de
componentes, sendo cada componente implementado como um subalgoritmo.
Dois tipos de subalgoritmos vão ser analisados:
•
Funções (Functions)
•
Procedimentos (Procedures)
As funções definidas pelo programador têm uma estrutura semelhante à dos
procedimentos, apenas com a obrigação de devolver um determinado resultado.
Isto vai implicar que o modo como uma função é declarada e chamada num
algoritmo é ligeiramente diferente do que se passa num procedimento.
Os parâmetros nos procedimentos podem ser de input ou de output, mas não
são sempre de input ou output, exclusivamente. Muitas vezes servem para transferir
informação para o subalgoritmo, funcionando como parâmetro de input, mas
recebem um novo valor do subalgoritmo, que devolvem ao ponto de chamada,
servindo então como parâmetro de output. Tais parâmetros são chamados
parâmetros de input-output.
A diferença entre uma função e um procedimento reflecte-se também no modo
como cada um destes dois tipos de subalgoritmos é chamado, nomeadamente:
•
quando se trata de um procedimento, apenas tem que se escrever o
respectivo identificador, com os eventuais argumentos que ele exija;
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
12
Unidade I
•
Algoritmos
quando se trata de uma função, torna-se necessário incluí-la numa instrução
de escrita ou de atribuição.
Uma vantagem importantíssima dos subalgoritmos é que eles podem ser
chamados quantas vezes quisermos dentro de um algoritmo. Podemos assim
efectuar a mesma operação ou conjunto de operações em diferentes pontos de um
algoritmo.
Mas, a vantagem dos subalgoritmos vai mais longe: podemos querer efectuar,
em diferentes pontos de um algoritmo, o mesmo tipo de operações (o mesmo
subalgoritmo) mas com dados diferentes. Aí, surgem-nos novos elementos que os
subalgoritmos podem utilizar - os parâmetros.
Os parâmetros são elementos semelhantes às variáveis, mas que são inseridos
nos cabeçalhos dos subalgoritmos (procedimentos e funções) e que, depois, são
usados nas chamadas a esses mesmos subalgoritmos. Os valores indicados no
lugar dos parâmetros, quando se faz uma chamada a um subalgoritmo, são
chamados argumentos (valor).
Uma subalgoritmo pode conter mais do que um parâmetro (inseridos dentro de
parêntesis). Na altura em que é feita a chamada ao subalgoritmo, é tida em
consideração a ordem dos argumentos, bem como o tipo de dados a que pertencem
e são necessários tantos argumentos quantos os parâmetros. Os argumentos
utilizados nas chamadas a subalgoritmos podem ser não apenas valores directos,
mas também variáveis e expressões, desde que os valores sejam compatíveis com
os correspondentes parâmetros.
Existem um conjunto de funções intrínsecas normalmente disponíveis na maior
parte das linguagens de programação, tais como:
•
Função TRUNC(X) - retorna o valor inteiro do seu argumento. Nota: O
número A é múltiplo de B, se TRUNC(A/B) = A/B
•
Função ABS(X) - retorna o valor absoluto do seu argumento
•
Função SQRT(X) - retorna o quadrado do seu argumento
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
13
Unidade I
5.1.
Algoritmos
Exemplos
Função MÉDIA
Função MÉDIA(VALOR1, VALOR2, VALOR3)
Esta função calcula a média dos três valores fornecidos pelos três parâmetros de input.
MED é uma variável local. Todas as variáveis são reais.
1. [Calcula a média]
MED ! (VALOR1 + VALOR2 + VALOR3) / 3.0
2. [Devolve resultado e regressa ao ponto de chamada - Saída da função]
Regresso(MED)
Exemplo de utilização da função MÉDIA
Algoritmo TESTA_MÉDIA
Este algoritmo ilustra o uso da função MÉDIA com vários argumentos. Todas as
variáveis são reais.
1. [Inicializar valores de teste]
A ! 2.0
B ! 6.1
C ! 7.5
2. [Utiliza a função numa expressão]
D ! MÉDIA(A, B, C)
3. [Utiliza a função numa expressão mais complexa]
E ! D + MÉDIA(B, 3.2, A+7)
4. [Imprimir a média]
Escreva('A média dos três valores é: ',D)
5. [Terminar]
Saída
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
14
Unidade I
Algoritmos
Procedimento DIVIDE
Procedimento DIVIDE(DIVIDENDO, DIVISOR, QUOC, RESTO)
Este procedimento divide o parâmetro de input, DIVIDENDO pelo parâmetro DIVISOR
dando os parâmetros de output QUOC e RESTO. Todos os números são inteiros.
1. [Testa divisão]
SE DIVISOR = 0
Então Escreva('Divisão por zero. Impossível!')
Regresso
2. [Executar divisão de inteiros]
QUOC ! DIVIDENDO / DIVISOR
3. [Determina Resto]
RESTO ! DIVIDENDO - (QUOC * DIVISOR)
4. [Regressa ao ponto de chamada - Saída do procedimento]
Regresso
Exemplo de utilização do procedimento DIVIDE
Algoritmo TESTA_DIVIDE
Este algoritmo ilustra o uso do procedimento DIVIDE. Todas as variáveis são inteiros.
1. [Inicializar valores de teste]
VAL1 ! 5
VAL2 ! 3
2. [Chama procedimento para divisão]
Chama DIVIDE(VAL1, VAL2, QUOC1, RESTO1)
3. [Imprimir resultado]
Escreva('Quociente: ',QUOC1, 'Resto: ',RESTO1)
4. [Chama novamente o procedimento para divisão]
Chama DIVIDE(VAL1 * VAL2, VAL2 + 1, QUOC2, RESTO2)
5. [Imprimir o resultado]
Escreva('Quociente: ',QUOC2, 'Resto: ',RESTO2)
6. [Terminar]
Saída
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
15
II
ESTRUTURAS DE DADOS
1.
ESTRUTURA
DE
DADOS
Na programação de alto nível, sempre que se declaram variáveis, estas têm de
ser associadas a um determinado tipo de dados, para que o compilador saiba com
que tipo de valores vai operar e que espaço deve ser reservado em memória para
cada variável. Para além das estruturas de dados que mantenha em memória
primária, existem estruturas de dados que conserve em memória secundária, que é
o caso dos ficheiros.
Para além de uma categoria de dados a que podemos chamar de Simples,
temos ainda os dados que são estruturadas, isto é, que são compostos por outros
dados.
 Simples

Estrutura de Dados 
 Complexas ou Estruturadas

2.
ESTRUTURA
DE
DADOS SIMPLES
 Valores Lógicos

 Numéricas  Inteiros
Estrutura de Dado Simples 
 Reais
 Alfanuméricos : Caracteres e Cadeias de Caracteres

 Dinâmicas : Ponteiros
ESEV(Prof.Serv.) - Prof. José Alberto Pereira
16
Unidade II
2.1.
Estruturas de Dados
Valores lógicos
Os dados deste tipo, são dados que podem assumir apenas dois valores
possíveis: verdadeiro e falso. A sua utilidade reside, fundamentalmente, ao nível do
seu emprego em estruturas de controlo.
2.2.
Inteiros
Este tipo de dados é, na verdade, um subconjunto dos inteiros, que constituem,
como se sabe, um conjunto infinito. Como os dados em computação ocupam espaço
não podemos trabalhar com conjuntos infinitos. Assim, os inteiros variam num
intervalo de acordo com o número de bits para a sua representação no sistema
informático na forma de dígitos binários.
•
n bits " 2n valores diferentes
•
Representação no sistema binário dos números não negativos: 0 ≤ x ≤ 2n-1
- Uma palavra de n bits permite representar todos os inteiros não negativos compreendidos
n
entre 0 e (2 -1).
•
Representação dos números negativos:
-
Código de sinal e valor absoluto: -(2n-1-1) ≤ x ≤ (2n-1-1)
Obtém-se o código de sinal e valor absoluto colocando à esquerda dos números binários
que pretendemos afectar de sinal um bit que terá o valor "0" se se pretender representa um
sinal positivo e terá o valor de "1" se se pretender representar o sinal negativo. O referido
bit é denominado o bit de sinal. Ao representar este tipo de representação num sistema
caracterizado por uma palavra de n bits reserva-se o bit mais à esquerda para exprimir o
sinal e utilizam-se os restantes (n-1) bits para representar o valor absoluto. Deste modo,
n-1
uma palavra de n bits permite representar todos os inteiros compreendidos entre -(2 -1) e
n-1
(2 -1), incluindo o zero que tem duas representações: uma para +0 e outra para -0. O total
n
de números distintos que se podem representar por este processo é de (2 -1), incluindo o
zero.
- Notação dos complementos de um: -(2n-1-1) ≤ x ≤ (2n-1-1)
-
Complementos de dois: -(2n-1) ≤ x ≤ (2n-1-1)
Tem representação única para o zero.
- Código Binário Deslocado: -(2n-1) ≤ x ≤ (2n-1-1)
- etc.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
17
Unidade II
Estruturas de Dados
Ex.: Para se representar um inteiro padrão em Pascal utilizam-se dois bytes no
tipo Integer (16 bits). O tipo Integer em Pascal, representa os inteiros em
Complemento de dois. Assim, os inteiros do conjunto Integer variam no intervalo:
[-216-1, +216-1-1] = [-32768, +32767]. O tipo Longint representa o tipo de inteiros
maior, que utiliza 4 bytes, o que permite definir o intervalo [-2147483647,
+2147483647]. O tipo Word que comporta apenas os números não negativos,
definido por 2 bytes: [0, +65535]. O tipo Shortint, um tipo de inteiros menor, que
utiliza apenas 1 byte. O intervalo deste subtipo é: [-127, +128]. O tipo Byte, um outro
conjunto de inteiros definido também só por um byte, constituído só por positivos e
zero: [0, +255].
2.3.
Reais
Os dados do tipo real representam aproximadamente os números reais, portanto,
números que podem ter partes decimais.
O esquema da representação interna dos reais, a nível da memória e do
processador, como se sabe, é feita em números binários. A conversão de números
decimais para binários, tratando-se de reais, implica uma certa complexidade, num
esquema a que se costuma chamar representação em Vírgula flutuante. Uma parte
do número é chamado mantissa ou fracção (conjunto dos dígitos significativos), e
uma outra é o expoente. Existe um outro formato designado por vírgula fixa, mas
levanta várias dificuldades ao programador.
Representação de números reais, em virgula flutuante:
Sinal Mantissa * Base expoente
Esta notação é, afinal, aquela a que estamos habituados em cálculo científico.
O expoente pode ser positivo ou negativo, conforme a grandeza do número a
representar.
Por exemplo, um número real pode surgir com este formato: 1.567893E3
(notação em linguagem Pascal). A primeira parte do número, até à letra E, é a
mantissa; o número depois da letra E representa o expoente de base 10 a que o
número é elevado; assim, o número apresentado acima é equivalente a: 23.6 x 107
(notação científica). Como é evidente, esta representação não é única. Podemos
escrever igualmente, por exemplo: 2360 x 105. Para evitar estas ambiguidades, os
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
18
Unidade II
Estruturas de Dados
formatos em virgula flutuante utilizam, de entre todas as representações possíveis,
aquela que contém mais algarismos significativos, denominada representação
normalizada. Para normalizar um número expresso pela sua mantissa e pelo seu
expoente, deslocam-se os dígitos da mantissa para a esquerda até que o primeiro
dígito seja significativo, e altera-se o valor do expoente de tantas unidades quantos
os deslocamentos realizados. Exemplo de operação de normalização: 000236 x 1006
passaria a ser 236000 x 1003.
Como resultado da normalização, a mantissa é sempre enquadrada de tal forma
que as ultrapassagens de capacidade apenas se verificam no expoente.
É fácil de verificar que o número de dígitos da mantissa está directamente ligado
à precisão dos cálculos, enquanto que o números de dígitos do expoente determina
os números extremos que a máquina pode representar.
Existem várias convenções para representar os números flutuantes à custa de
uma palavra binária. A convenção mais corrente coloca as informações mais
significativas nas posições mais significativas: primeiro o sinal, depois o expoente, e
finalmente a mantissa:
Sinal
Expoente
Mantissa
O sinal + é representado por zero, e o sinal - por um 1. O expoente exprime
geralmente uma potência de 16. A mantissa pode ser considerada como inteira
supondo-se que a vírgula se encontra imediatamente à direita, ou como fraccionária,
supondo-se então que a vírgula se encontra imediatamente à esquerda.
Os formatos de vírgula flutuante implementados pela INTEL no seu processador
aritmético 80387 e no microprocessador 80486, seguem a norma de virgula flutuante
IEEE 754-195, publicada em 1985. São ao todo três formatos: de precisão simples,
de precisão dupla e de precisão expandida, que ocupam, respectivamente, 4, 8 e 10
bytes.
Precisão
simples
1
8
23
Sinal
Expoente
Mantissa
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
32 bits = 4 bytes
19
Unidade II
Estruturas de Dados
Precisão
dupla
Precisão
expandida
2.4.
1
11
Sinal
Expoente
1
15
Sinal
52
Mantissa
64 bits = 8 bytes
64
Expoente
Mantissa
80 bits = 10 bytes
Alfanuméricos
Existem actualmente vários códigos susceptíveis de ser utilizados pelos
computadores digitais. Alguns desses códigos permitem representar, não apenas
grandezas numéricas, mas também caracteres literais e sinais de pontuação. É
exemplo o código ASCII ("American Standard Code for Information Interchange" leia-se "ásqui"), usado na transferência de informação entre um computador e os
seus dispositivos periféricos. Trata-se de um código que utiliza combinações de oito
bits (28 = 256) para representar os dez algarismos decimais (0, 1, 2, ..., 9), os
caracteres do alfabeto, sinais de pontuação, parêntesis, sinais das operações
aritméticas e de igualdade, e mais uma série de caracteres de controlo, entre outros
símbolos, num total de 256 símbolos.
Os códigos que se destinam a traduzir, não só informação numérica, mas
também caracteres do alfabeto e sinais convencionais são denominados códigos
alfanuméricos. O código ASCII, referido acima, representa o código alfanumérico
universalmente aceite.
Os dados do tipo carácter (Char em Pascal) correspondem a caracteres
individuais. Os caracteres disponíveis são geralmente os caracteres da Tabela
ASCII. Este tipo de dado pode assumir qualquer carácter da referida Tabela ASCII,
mas apenas um de cada vez. Por curiosidade, existe rotinas para converter um dado
número inteiro para o correspondente carácter na Tabela ASCII. Por exemplo, a
função CHR, em Pascal. Por sua vez, existe rotinas que dá o ordinal de um dado
carácter, por exemplo a função ORD em Pascal.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
20
Unidade II
Estruturas de Dados
Para facilitar a manipulação de palavras ou mensagens, dentro de um programa,
existe outro tipo de dados: Cadeia de caracteres (Cadeia Alfanumérica). Estes tipos
de dados são mais adequados ao manuseamento de texto, nas instruções de leitura,
escrita e atribuição. Em Pascal este tipo de dados é representado pelo tipo de
variável: String.
Um dado do tipo String que tenha um número determinado de caracteres, pode
ser tratado como um vector do tipo Carácter (Char). Saber que podemos trabalhar
com Strings como vectores de caracteres, é útil, porque, por exemplo podemos
manipular as cadeias de caracteres com outros recursos de programação. Por
exemplo, podemos pretender um algoritmo que, dada uma determinada palavra,
introduzida pelo utilizador, seja capaz de contar quantas vogais existem nessa
palavra.
2.5.
Ponteiros
O tipo de dados Ponteiro ou Apontador entra já num conceito diferente. Nas
variáveis deste tipo, não figuram valores directos, mas números que apontam para
endereços da memória.
3.
ESTRUTURA
DE
DADOS COMPLEXAS

 Vectores
Comprimento fixo 
Matrizes



 Registos
Estruturas de Dados Complexas 

Pilhas




Lineares Filas
 Comprimento variável : Listas 
Ficheiros




Não Lineares : Árvores


3.1.
Vectores
Um vector (Array unidimensional) é um tipo estruturado que pode agrupar
numa mesma variável um conjunto finito de valores todos do mesmo tipo. Um vector
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
21
Unidade II
Estruturas de Dados
é um conjunto de elementos representados por um identificador e um único índice.
Cada elemento tem uma única dimensão. O índice varia entre um limite inferior e um
limite superior, em correspondência com o número de elementos do conjunto. Os
vectores são colocados na memória em posições ordenadas e adjacentes.
Suponhamos que pretendemos representar, num programa, os gastos de um
determinado departamento em cada um dos 12 meses do ano. Evidentemente,
poderíamos definir 12 variáveis, designadas por 12 identificadores diferentes; por
exemplo: JAN; FEV; MAR; etc. Todavia, o uso de uma variável estruturada, neste
caso, um vector, com um único identificador agrupando os 12 elementos em causa,
revela-se uma técnica muito mais económica em termos de escrita, mas, sobretudo,
encerra muito mais potencialidades de manipulação em termos de programação.
Neste caso, poderíamos definir um vector mediante um único identificador, por
exemplo: GASTOS_MES
Mas, para que esse identificador possa representar os 12 elementos
correspondentes aos 12 meses do ano, temos de utilizar índices.
Em Pseudocódigo, poderíamos escrever assim:
GASTOS_MES: Vector[1,...,12] de Real
Em que:
•
GASTOS_MES - é o identificador ou nome atribuído à variável;
•
Vector - indica que a variável é do tipo Vector;
•
[1,...,12] - define o número de elementos da variável e ao mesmo tempo o
intervalo dos seus índices, neste caso entre 1 e 12;
•
de Real - indica qual o tipo de dados dos elementos do vector.
ou ainda
GASTOS_MES[12]
De um modo geral, cada elemento desta variável de tipo Vector designa-se por:
GASTOS_MES[K]
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
22
Unidade II
Estruturas de Dados
Em que [K] representa a posição do elemento no conjunto que compõem o
vector, ou seja, neste caso, o número do mês que se pretende designar. Por
exemplo o gasto do mês de Fevereiro seria designados por: GASTOS_MES[2]
Segue-se alguns exemplos de operações com vectores:
[Definir um vector A de 40 elementos do tipo inteiro]
A: Vector[1, ..., 40] de Inteiro
[Instrução de leitura com acesso sequencial]
Repita para K=1,2,...,40
Leia(A[K])
[Instrução de atribuição: armazenar na 4ª posição do vector A o valor 13]
A[4] ! 13
[Instrução de escrita do 4ª elemento do vector A]
Escreva(A[4])
3.2.
Matrizes
Uma matriz (Array multidimensional) é um tipo estruturado que pode agrupar
numa mesma variável um conjunto finito de valores todos do mesmo tipo.
No caso dos vectores, temos apenas um agrupamento de elementos, cujos
índices estão compreendidos entre dois limites; nas matrizes temos dois (ou mais)
elementos, cada qual com o seu par de limites próprio.
Uma matriz é um conjunto de elementos representados por um identificador e
dois índices. Da mesma forma do que os vectores, os dois índices varia entre um
limite inferior e um limite superior, em correspondência com o número de elementos
do conjunto.
Segue-se alguns exemplos de operações com matrizes:
[Definir uma matriz M com elementos do tipo inteiro]
M: Matriz[1, ..., 10; 1, ..., 5] de Inteiro
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
23
Unidade II
Estruturas de Dados
[Instrução de leitura com acesso sequencial]
Repita para K=1,2,...,10
Repita para L=1,2,...,5
Leia(M[K, L])
[Instrução de atribuição]
M[4, 3] ! 20
3.3.
Registos
Os registos (Records) são um outro tipo de dados estruturados que permitem
agrupar elementos de vários tipos diferentes, sob a forma de campos.
A principal diferença que costuma apontar-se entre um vector/matriz é que,
quando um vector/matriz agrupa um conjunto de dados do mesmo tipo, um registo
pode conjugar diferentes tipos de dados na mesma estrutura. Todavia, existem
outras diferenças importantes, sobretudo no modo de acesso aos elementos de um
e de outro tipo de estrutura.
De facto, o que interessa sobretudo é a maneira como se vai aceder a essa
informação estruturada. Se o problema exige que o acesso seja por "indexação", isto
é, do género, "toma lá um índice, dá cá um valor", então o que faz falta é um
vector/matriz; se, por qualquer motivo, o que queremos é um acesso por
"nomeação" - "passa para cá a informação com este nome" - então o melhor é ir
pelos registos.
Um exemplo típico de dados organizáveis sob a forma de registos é o que se
relaciona com informação sobre pessoas. Os seguintes dados poderiam ser os
campos de um registo: nome; morada; idade; telefone; etc.
Em pseudocódigo seria:
[Registo que inclui os campos: Nome, Morada, Idade e Telefone]
DADOS_PESSOA de Registo
NOME
MORADA
IDADE
TELEFONE
Fim
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
24
Unidade II
3.4.
Estruturas de Dados
Listas
Um Lista é um conjunto ordenado consistindo num número variável de
elementos aos quais se podem fazer inserções e eliminações.
 lineares
Listas 
 não lineares
Uma lista linear é uma Lista que apresenta a propriedade de adjacência entre os
seus elementos.
Algumas operações a realizar com Listas:
•
Inserir um elemento na lista
•
Eliminar um elemento na lista
•
Remover um elemento na lista
•
Fundir duas ou mais listas numa só
•
Formar várias listas a partir de uma só
•
Copiar uma lista
•
Determinar o comprimento de uma lista, ou seja, o número de elementos de
uma lista
•
Ordenar os elementos de uma lista
•
Pesquisar um elemento na lista, que contenha um campo com um certo valor
•
etc.
3.5.
Pilhas
Uma pilha é uma lista linear na qual todas as eliminações e inserções, e
normalmente todos os acessos, são feitos num extremo da lista. Uma pilha (Stack) é
uma colecção ordenada de elementos sendo os novos elementos adicionados ou
elementos retirados a partir de uma das suas extremidades, designada por "Topo"
da pilha. Uma pilha é uma lista linear na qual todas as eliminações e inserções, e
normalmente todos os seus acessos, são feitos num extremo da lista.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
25
Unidade II
Estruturas de Dados
Topo da pilha
C
E
A
1º elemento da pilha
A pilha é uma estrutura dinâmica, ou seja, de comprimento variável. A pilha é
também designada por uma estrutura LIFO ("Last-In", "First-Out"), o último e chegar
é o primeiro a sair. Na verdade a informação que se tem de uma pilha é o elemento
que está no topo.
Representação de pilhas em pascal
Há várias formas de representar uma pilha em pascal. Uma forma simples de
representar uma pilha é usar um vector. Nota-se contudo que um vector e uma pilha
são duas entidades extremamente diferentes, pois o número de elementos (máximo)
é fixo, enquanto que numa pilha esse número não existe. Uma pilha é uma entidade
dinâmica cuja dimensão varia constantemente. Temos então que declarar um vector
com uma dimensão suficientemente grande. Uma das "extremidades" do vector
corresponderá à "extremidade" da pilha que nunca varia.
É ainda necessário definir um outro campo que indicará qual a posição corrente
do topo da pilha.
Pode-se então declarar-se uma pilha usando um registo (record) contendo dois
campos: um vector (array) contendo os elementos da pilha; um inteiro para indicar a
posição do topo da pilha actual.
Algumas operações sobre Pilhas:
•
Procedure PUSH(S, TOP, X) - adiciona (ou empilha) um novo elemento á
pilha, dado um elemento "X" e uma pilha "S". PUSH adiciona "X" ao topo da
pilha "S".
•
Function POP(S, TOP) - Remove o elemento que está correctamente no topo
da pilha: X ! POP(S, TOP).
Quando se retira o último elemento de uma pilha, a pilha diz-se vazia. Não se
pode, portanto, aplicar-se sempre a operação POP a uma pilha, ao contrário do que
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
26
Unidade II
Estruturas de Dados
acontece com a operação PUSH. É necessário saber se uma pilha ainda tem
elementos para retirar ou não. É necessário definir uma função EMPTY.
•
Function EMPTY(S) - Determina se uma pilha está ou não vazia. Se vazia
EMPTY devolve o valor verdadeiro.
É ainda útil definir uma outra função STACK_TOP(S) que permite determinar
qual é o elemento que está no topo da pilha sem contudo o remover da pilha.
•
Function STACK_TOP(S)
Esta função é equivalente a:
X ! POP (S, TOP)
PUSH (S, TOP, X)
Tal como POP, STACK_TOP não é definida para uma pilha vazia.
TOP representa o elemento localizado no topo da pilha. Inicialmente quando a
pilha está vazia o valor de TOP é zero. De cada vez que se insere um elemento na
pilha, POP é incrementado de uma unidade, antes do elemento ser colocado na
pilha. POP é decrementado de uma unidade sempre que se elimina um elemento da
pilha.
Pseudocódigo de algumas operações sobre Pilhas:
Procedure PUSH(S, TOP, X)
Este procedimento empilha um elemento X no topo duma pilha que é representada por
um vector S com N elementos. O elemento no topo da pilha é indicado por um ponteiro TOP.
1. [Verificar se falta capacidade da pilha]
Se TOP ≥ N
Então Escreva('A pilha não suporta mais elementos - Overflow')
Regresso
2. [Incrementar o ponteiro do topo da pilha]
TOP ! TOP + 1
3. [Inserção do elemento na pilha]
S[TOP] ! X
4. [Terminar]
Regresso
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
27
Unidade II
Estruturas de Dados
Function POP(S, TOP)
Esta função retira o elemento do topo da pilha que é representada por um vector S e
devolve esse elemento. TOP é um ponteiro para o elemento no topo da pilha.
1. [Verificar se a pilha está vazia]
Se TOP = 0
Então Escreva('A pilha está vazia - Underflow')
Regresso
2. [Guarda o valor do elemento a eliminar]
Y ! S[TOP]
3. [Decrementar o ponteiro do topo da pilha]
TOP ! TOP - 1
4. [Devolve o valor do elemento que estava no topo da pilha e termina]
Regresso(Y)
Function STACK_TOP(S)
Dados um vector S, com N elementos e cujo elemento do topo é indicado por TOP, essa
função devolve o elemento do topo da pilha.
1. [Verificar se a pilha está vazia]
Se TOP = 0
Então Escreva('A pilha está vazia')
Regresso
2. [Devolve o elemento que está no topo da pilha e termina]
Regresso(S[TOP])
3.6.
Filas
Uma fila (queue) é uma lista linear na qual todas as inserções são feitas num
extremo da lista (a parte de trás) e todas as eliminações, e normalmente todos os
acessos, são feitos no outro extremo da lista (parte da frente).
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
28
Unidade II
Estruturas de Dados
1
2
3
4
5
6
A
B
C
D
E
F
Frente
Retaguarda
Eliminar
Inserir
A fila é uma estrutura dinâmica, ou seja, de comprimento variável. A fila é
também designada por uma estrutura FIFO ("First-In", "First-Out"), o primeiro a
chegar é o primeiro a sair. Só os elementos que estão á frente podem ser apagados
e só se podem adicionar elementos na parte de trás.
Um exemplo familiar de uma fila é a fila de clientes de um supermercado junto às
caixas registadoras. A primeira pessoa na fila é a primeira a ser atendida.
Representação de filas em pascal
Apesar de uma fila ser uma entidade dinâmica, as suas operações podem ser
simuladas utilizando um vector com um número suficientemente grande de
elementos que nos permita manter a propriedade de comprimento variável. A
representação vectorial Q[1, ..., N] (vector de N elementos) de uma fila requer o uso
de dois ponteiros F e R, que representam a posição do elemento da Frente (primeiro
elemento da fila) e da Retaguarda (último elemento da fila), respectivamente.
Pode-se então declarar-se uma fila usando um registo (record) contendo três
campos: um vector contendo os elementos da lista; um inteiro para indicar a posição
do elemento da frente e outro para indicar o elemento da retaguarda.
Ignorando, para já, a possibilidade de ocorrência de overflow e underflow, as
operações de inserção e remoção poderiam ser implementadas do seguinte modo:
[Inserção de um elemento Y: Incrementa primeiro R e depois insere o elemento]
R!R+1
Q[R] ! Y
[Remoção de um elemento Y: Primeiro remove o elemento e depois incrementa F]
Y ! Q[F]
F!F+1
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
29
Unidade II
Estruturas de Dados
Operações primitivas sobre Filas:
•
Procedure QINSERT(Q, F, R, N, Y) - Insere um novo elemento Y á fila na
parte de trás da fila Q de N elementos.
•
Function QDELETE(Q, F, R, N) - Elimina o elemento da frente da fila Q de N
elementos.
•
Function EMPTY(Q) - Determina se a fila contém ou não elementos. Se vazia
EMPTY devolve o valor verdadeiro.
Quando se retira o último elemento de uma fila, a fila diz-se vazia. Não se pode,
portanto, aplicar-se sempre a operação QDELETE a uma fila, ao contrário do que
acontece com a operação QINSERT.
Inicialmente R é 0 e F é 1. A fila está vazia enquanto R < F. O número de
elementos da fila será R-F+1. Se R = F só temos um elemento na fila.
Consideramos uma fila com máximo de 5 elementos (vector de comprimento 5):
1
2
3
4
5
A
B
C
1
2
3
4
5
1ª Situação
2ª Situação
F=1 e R=0
F=1 e R=3
Fila Vazia: R<F
Inserir 3 elementos
C
1
2
3
4
5
1
2
C
D
E
3
4
5
3ª Situação
4ª Situação
F=R=3
F=3 e R=5
Remover 2 elementos
Inserir 2 elementos
Pseudocódigo com este tipo de implementação:
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
30
Unidade II
Estruturas de Dados
Procedure QINSERT(Q, F, R, N, Y)
Dados F e R, ponteiros para os elementos da frente e retaguarda da fila, uma fila
vectorial Q com N elementos e um elemento Y, este procedimento insere Y na parte de trás da
fila. Antes do 1º uso do procedimento, F e R são inicializados a 0 e 1 respectivamente (F ! 1,
R ! 0).
1. [Verificar se excedeu a capacidade da fila]
Se R ≥ N
Então Escreva('A fila não suporta mais elementos - Overflow')
Regresso
2. [Incrementar o ponteiro de trás]
R!R+1
3. [Inserção do elemento na fila]
Q[R] ! Y
4. [Terminar]
Regresso
Function QDELETE(Q, F, R, N)
Dados F e R, ponteiros para os elementos da frente e retaguarda da fila, uma fila
vectorial Q com N elementos, esta função elimina e devolve o último elemento da fila (na
parte da frente da fila). Y é uma variável temporária.
1. [Verificar se a fila está vazia]
Se R < F
Então Escreva('A fila não tem elementos - Underflow')
Regresso
2. [Eliminar o elemento na fila]
Y ! Q[F]
3. [Incrementar o ponteiro da frente]
F!F+1
4. [Devolve o elemento e terminar]
Regresso(Y)
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
31
Unidade II
Estruturas de Dados
Se a fila exceder a sua capacidade e posteriormente efectuarmos sucessivas
operações até remover todos os seus elementos, esta implementação não permite
operações de inserção, apesar de a fila estar vazia. O problema desta
implementação é que não permite reutilizar a fila quando ela ficar vazia depois de ter
atingido a sua capacidade máxima.
O problema pode ser resolvido inicializando os apontadores R e F a zero e na
operação remoção, cada vez que a fila ficar vazia, reinicializar a zero os respectivos
apontadores. A fila estaria vazia para F=0 e o pseudocódigo passaria a ser o
seguinte:
Procedure QINSERT(Q, F, R, N, Y)
Dados os ponteiros F e R para a frente e retaguarda duma fila Q com N elementos e um
elemento Y, este procedimento insere Y na retaguarda da fila. Antes do 1º uso do
procedimento, F e R são limpos (F ! 0, R ! 0).
1. [Verificar se excedeu a capacidade da fila]
Se R ≥ N
Então Escreva('A fila não suporta mais elementos - Overflow')
Regresso
2. [Incrementar o ponteiro de trás]
R!R+1
3. [Inserção do elemento na fila]
Q[R] ! Y
4. [O ponteiro da frente tem o valor correcto? Necessário na inserção do 1º elemento da
fila]
Se F = 0
Então F ! 1
5. [Terminar]
Regresso
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
32
Unidade II
Estruturas de Dados
Function QDELETE(Q, F, R, N)
Dados os ponteiros F e R para a frente e retaguarda duma fila Q com N elementos, esta
função elimina e devolve o último elemento da fila (na parte da frente da fila). Y é uma
variável temporária.
1. [Verificar se a fila está vazia]
Se F = 0
Então Escreva('A fila não tem elementos - Underflow')
Regresso
2. [Eliminar o elemento na fila]
Y ! Q[F]
3. [Fila Vazia? Senão Incrementa o ponteiro da frente]
Se F = R
Então F ! 0
R!0
Senão F ! F + 1
4. [Devolve o elemento e terminar]
Regresso(Y)
O problema com estas duas possíveis implementações é que o vector pode ter
até 5 elementos e no entanto a fila não pode crescer mais, pois R teria que assumir
o valor 6, que corresponde ao índice do vector. Ou seja, a fila pode não crescer mais
(overflow) ao tentarmos inserir um elemento apesar de algumas das primeiras
localizações não estarem a ser usadas.
Uma solução possível, seria alterar a operação de remoção de um elemento, de
modo a que sempre que um elemento seja apagado, toda a fila seja deslocada para
o início do vector. Deixaria de ser necessário especificar o campo F, pois, a frente da
fila passaria a ser sempre o primeiro elemento do vector. Mas este método é
ineficiente, pois , cada vez que um elemento é removido, todos os outros têm que
ser deslocados.
A solução mais eficiente de todos estes problemas é tratar o vector que contem
os elementos da fila como sendo circular e não um segmento de recta, ou seja, o
primeiro elemento do vector segue-se ao seu ultimo elemento.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
33
Unidade II
Estruturas de Dados
Com este tipo de implementação a condição R < F não serve para determinar se
a fila está vazia. Uma possível solução é convencionar que F é o índice do elemento
do vector que precede o 1º elemento da fila e R é o índice do elemento da
retaguarda do vector. Assim o teste de fila vazia seria: F=R. Os ponteiros F e R são
inicializados no último índice do vector, pois F precede o primeiro nesta
representação.
Antes da inserção de um elemento na fila, quando o vector está totalmente
preenchido, verifica-se a condição F = R que é utilizada para verificar a ocorrência
de underflow (fila vazia). O problema é que deste modo não há possibilidade de
distinguir a situação de underflow da situação de overflow. A solução deste
problema, é permitir que a fila cresça apenas até um número de elementos igual ao
total do vector menos um. Para isso na implementação da inserção implementa-se o
ponteiro R antes de efectuar o teste F=R.
Ignorando, para já, a possibilidade de ocorrência de overflow e underflow, as
operações de inserção e remoção para uma fila circular poderiam ser
implementadas do seguinte modo:
[Inserção de um elemento Y]
R!R+1
Q[R] ! Y
[Remoção de um elemento Y]
F!F+1
Y ! Q[F]
Antes de um dado elemento ser introduzido na fila é necessário utilizar o teste de
fila cheia. Antes de um elemento ser retirado da fila é necessário verificar se esta é
possível, recorrendo ao teste de fila vazia.
Consideramos como exemplo, uma fila circular com o máximo de 5 elementos
(vector de comprimento 5):
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
34
Unidade II
Estruturas de Dados
1
1
A
5
2
5
2
4
3
4
3
1ª Situação
2ª Situação
F=R=5
F=5 e R=1
Fila Vazia - Underflow
Inserir 1 elemento
F precede o 1º elemento
1
1
F
E
2
C
F
5
5
E
2
D
4
3
4
3
5ª Situação
6ª Situação
F=2 e R=1
F=4 e R=1
Inserir 2 elementos
Remover 2 elementos
Fila cheia - Overflow
1
1
A
2
5
5
1
2
B
C
D
3
C
4
D
3
4
3ª Situação
4ª Situação
F=5 e R=4
F=2 e R=4
Inserir 3 elementos
Remover 2 elementos
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
35
Unidade II
Estruturas de Dados
Pseudocódigo da implementação da fila com um vector circular:
Procedure QINSERT(Q, N, F, R, Y)
Dado o ponteiro F que precede o 1º elemento da fila (da frente) e o ponteiro R da
retaguarda duma fila circular Q de N elementos, e um elemento Y, este procedimento insere Y
na retaguarda da fila. Os ponteiros F e R são inicializados no último índice do vector:
(F ! N, R ! N).
1. [Guardar o ponteiro de trás, caso se exceda a capacidade]
TEMP ! R
2. [Reposiciona o ponteiro de trás]
Se R=N
Então R ! 1
Senão R ! R + 1
3. [Verificar se excedeu a capacidade da fila]
Se R = F
Então Escreva('A fila não suporta mais elementos - Overflow')
R ! TEMP
Regresso
4. [Inserção do elemento na fila]
Q[R] ! Y
5. [Terminar]
Regresso
Function QDELETE(Q,, F, R, N)
Dado o ponteiro F que precede o 1º elemento da fila (da frente) e o ponteiro R da
retaguarda duma fila circular Q de N elementos, esta função elimina e devolve o último
elemento da fila (na parte da frente da fila). Y é uma variável temporária.
1. [Verificar se a fila está vazia]
Se R = F
Então Escreva('A fila não tem elementos - Underflow')
Regresso
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
36
Unidade II
Estruturas de Dados
2. [Reposiciona o ponteiro da frente]
Se F=N
Então F ! 1
Senão F ! F + 1
3. [Eliminar o elemento na fila]
Y ! Q[F]
4. [Devolve o elemento e terminar]
Regresso(Y)
Esta implementação tem a pequena desvantagem de a fila crescer apenas até
um número de elementos igual ao total do vector menos um. A solução para este
pequeno problema é resolvido pelo pseudocódigo seguinte:
Procedure QINSERT(Q, N, F, R, Y)
Dado os ponteiros F e R da frente e da retaguarda duma fila circular Q de N elementos, e
um elemento Y, este procedimento insere Y na retaguarda da fila. Inicialmente F e R são
limpos: (F ! 0, R ! 0).
1. [Guardar o ponteiro de trás, caso se exceda a capacidade]
TEMP ! R
2. [Reposiciona o ponteiro de trás]
Se R=N
Então R ! 1
Senão R ! R + 1
3. [Verificar se excedeu a capacidade da fila]
Se R = F
Então Escreva('A fila não suporta mais elementos - Overflow')
R ! TEMP
Regresso
4. [Inserção do elemento na fila]
Q[R] ! Y
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
37
Unidade II
Estruturas de Dados
5. [O ponteiro da frente está colocado correctamente? (necessário na inserção do 1º
elemento da fila]
Se F = 0
Então F ! 1
6. [Terminar]
Regresso
Function QDELETE(Q, F, R, N)
Dado os ponteiros F e R da frente e da retaguarda duma fila circular Q de N elementos,
esta função elimina e devolve o último elemento da fila (na parte da frente da fila). Y é uma
variável temporária.
1. [Verificar se a fila está vazia]
Se F = 0
Então Escreva('A fila não tem elementos - Underflow')
Regresso
2. [Eliminar o elemento na fila]
Y ! Q[F]
3. [Fila Vazia? Se sim devolve o elemento e termina. (a fila tinha um só elemento)]
Se F=R
Então F ! 0
R!0
Regresso(Y)
4. [Reposiciona o ponteiro da frente]
Se F=N
Então F ! 1
Senão F ! F + 1
5. [Devolve o elemento e terminar]
Regresso(Y)
3.7.
Listas Ligadas
Atendendo à definição de lista ligada (linked list), muitos autores chamam à lista
ligada, lista simplesmente ligada, ou ainda, lista ligada encadeada.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
38
Unidade II
Estruturas de Dados
A utilização de armazenamento sequencial, por meio de um vector, para
representar pilhas e filas tem desvantagens:
•
uma quantidade fixa de espaço de memória está reservada para a pilha ou fila
mesmo se estas estão praticamente vazias;
•
tamanho máximo dos vectores introduz a possibilidade de overflow.
Numa representação sequencial o ordenamento dos elementos de uma pilha ou
fila é implícito e determinado pelo índice do vector. A solução para estes problemas,
é uma ordenação explícita dos elementos, fazendo com que cada elemento
contenha o endereço do seguinte. Estamos perante uma lista simplesmente ligada.
Numa lista simplesmente ligada os seus componentes estão ligados entre si por
meios de ponteiros ou apontadores (pointers). A ideia básica de uma lista ligada é
de que cada componente individual da lista contenha um apontador que indique
onde o próximo componente pode ser encontrado. Portanto, a ordem relativa dos
componentes pode ser facilmente mudada, alterando simplesmente os apontadores.
Assim, uma lista ligada não está limitada a um número máximo de componentes. Na
verdade o número total de elementos disponíveis em cada instante é finito devido ás
limitações de capacidade de memória primária dos computadores.
Primeiro
elemento
A
Ponteiro de
Último
elemento
B
C
Informação
D
NIL
Ponteiro do
endereço seguinte
Início
Tem-se assim a estrutura de uma lista ligada, em que cada "caixa" representada
na figura dá-se o nome de nodo ou nó (node).
Cada elemento de uma lista ligada é um nó com dois campos: o da informação e
o campo do endereço do elemento seguinte. Este endereço é designado por
ponteiro. O último nodo da lista não tem sucessor, e consequentemente, não tem
nenhum endereço no campo do ponteiro. O ponteiro nil é utilizado para indicar o fim
da lista. Uma lista sem elementos é uma lista vazia. A lista é acedida por meio de
um ponteiro externo, que aponta para o primeiro nó.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
39
Unidade II
Estruturas de Dados
A estrutura de dados de um nó poderia ser a seguinte:
Ponteiro = Nome_Registo
Nome_Registo = Registo
Info: Tipo
Próximo: Ponteiro
Fim Registo
Desde que não seja dito nada ao contrário, o nó é composto por dois campos: o
campo da informação e o campo do ponteiro para o próximo nó.
A lista é uma estrutura dinâmica que pode crescer ou diminuir, não tendo um
número predeterminado de elementos.
Inserção de nós numa lista ligada
Suponhamos que se tem uma lista com 3 elementos com informação do tipo
inteiro:
List
5
3
8
NIL
Suponhamos que queremos juntar o inteiro 6 ao topo da lista. Para isso é
necessário obter um nó vazio para armazenar o inteiro 6.
Suponhamos que existe uma função que fornece nós vazios:
P ! CriarNodo
P é o endereço para o nó vazio.
p
List
5
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
3
8
NIL
40
Unidade II
Estruturas de Dados
É em seguida necessário inserir o inteiro 6 na parte Info do novo nó.
Convencionemos que a função seguinte o faz:
Info(p) ! 6
É necessário agora armazenar no outro campo o endereço ao índice da lista:
Próximo(p) ! List
Resultando:
p
6
List
5
3
8
NIL
É necessário mudar o valor de List para que este ponteiro passe o apontador
para o novo primeiro elemento da lista.
List ! p
List
6
5
3
8
NIL
O conjunto de operações foi a seguinte:
P ! CriarNodo
Info(p) ! 6
Próximo(p) ! List
List ! p
Remoção de nós numa lista ligada
Para remover o primeiro nó de uma lista não vazia é armazenado o valor do seu
campo Info numa variável temporária x.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
41
Unidade II
Estruturas de Dados
Suponhamos novamente, que se tem uma lista com 3 elementos com
informação do tipo inteiro e queremos remover o elemento com a informação 5:
List
5
3
8
NIL
8
NIL
8
NIL
P ! List
List
5
3
p
List ! Próximo(p)
x ! Info(p)
p
5
3
List
O nó para o qual p aponta é agora completamente inútil. Seria interessante
torná-la livre para outra utilização, assim como ao ponteiro p. Para isso usa-se a
seguinte função para libertar nó:
LibertarNodo(p)
List
3
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
8
NIL
42
Unidade II
Estruturas de Dados
Inserção de um elemento depois de um determinado nó
List
5
3
8
NIL
P
List
3
5
8
NIL
X
A inserção de um novo elemento numa lista, depois de determinado nó, envolve:
a atribuição de um nó, a inserção da informação e o ajuste de dois ponteiros. Se se
designar esta operação por InserirDepois(List, P, X) a inserção do elemento X, depois
do nó apontado por P, numa lista List, a sua implementação pode ser a seguinte:
q ! CriarNodo
Info(q) ! X
Próximo(q) ! Próximo(p)
Próximo(p) ! q
Um elemento só pode ser inserido depois de um determinado nó, e não antes do
nó. Isso porque numa lista não há forma de a partir de um nó, aceder ao nó
precedente.
Do mesmo modo para se apagar o nó de uma lista é insuficiente a informação do
ponteiro para esse nó. Para se apagar determinado nó é necessário mudar o campo
Próximo do nó que o precede de modo que ele fique a apontar para o nó seguinte.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
43
Unidade II
Estruturas de Dados
Implementação de uma pilha por meio de uma lista
Uma pilha só pode ser acedida pelo seu elemento de topo, assim como, a lista.
Seja uma pilha S, implementada por meio de uma lista:
S
3
2
1
NIL
A sua estrutura de dados em pascal, poderia ser a seguinte:
Type Pilha = ^NodoPilha;
NodoPilha = Record
Info: TipoDados;
Próximo: Pilha;
End;
Var S: Pilha;
Remover o primeiro elemento da lista é idêntico á execução da operação pop.
Uma implementação possível da operação Push(S, X), supondo que temos memória
suficiente no sistema, seria:
P ! CriarNodo
Info(p) ! X
Próximo(P) ! S
S!P
em que, S é o ponteiro de início da pilha e X o elemento a inserir.
A operação x ! Pop(S), pode ser implementada do seguinte modo:
Se PilhaVazia(S)
Então Escreva(‘Pilha vazia’)
Senão P ! S
S ! próximo(P)
X ! Info(P)
LibertarNodo(P)
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
44
Unidade II
Estruturas de Dados
em que, S é o ponteiro de início da pilha e PilhaVazia(S) é apenas um teste ao
ponteiro S (se é igual a nil).
Implementação de uma fila por meio de uma lista
O ponteiro da lista, que aponta para o primeiro elemento, representa a frente da
fila (ponteiro F). Outro ponteiro para o último elemento da lista representa a parte da
retaguarda da fila (ponteiro R).
Seja a fila Q, implementada por meio de uma lista:
Q
F
1
R
2
3
NIL
A sua estrutura de dados em pascal, poderia ser a seguinte:
Type Ponteiro = ^NodoFila;
NodoFila =
Record
Info: TipoDados;
Próximo: Ponteiro;
End;
Fila = Record
F: Ponteiro;
R: Ponteiro;
End;
Var Q: Fila;
Seja Q o ponteiro de início da fila e Frente(Q) uma função que acede à frente da
fila Q, e a função Retaguarda(Q) que acede à sua retaguarda. Uma implementação
da operação X ! QDelete(Q) de uma fila Q, que remove e devolve um elemento X da
fila Q (tira um nodo na frente da fila), poderia ser:
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
45
Unidade II
Estruturas de Dados
Se FilaVazia(Q)
Então Escreva(‘Fila Vazia’)
Senão P ! Frente(Q)
X ! Info(P)
Frente(Q) ! Próximo(P)
Se Frente(Q) = Nil
Então Retaguarda(Q) ! Nil
LibertaNodo(P)
A fila Q está vazia, quando ambos os ponteiros R e F estão nulos (Nil). A
operação QInserir(Q,, X), que insere um elemento X na fila Q (põe um nodo na
retaguarda da fila), pode ser implementada do seguinte modo:
P ! CriarNodo
Info(P) ! X
Próximo(P) ! Nil
Se Retaguarda(Q) = Nil
Então Frente(Q) ! P
Senão Próximo(Retaguarda(Q)) ! P
Retaguarda(Q) ! P
Quando a fila só tem um elemento, então os ponteiros R e F são iguais.
3.8.
Ficheiros
Os ficheiros têm como função, armazenar a informação em suportes de memória
secundária ou externas (discos ou disquetes, etc.), onde essa informação possa ser
guardada para além do tempo em que o programa está a correr no computador, e,
eventualmente, reutilizada, nesse ou em outro programa.
As restantes estruturas de dados, são dados voláteis, porque são armazenados
apenas temporariamente na RAM (memória primária) do computador, quando estes
estão a funcionar com o programa. Quando se sai do programa em que os dados
foram introduzidos ou obtidos, toda a informação desaparece.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
46
Unidade II
Estruturas de Dados
Assim, a unidade básica de armazenamento de informação em sistemas
informáticos é o ficheiro (file).Um ficheiro é uma unidade de informação, armazenada
fisicamente num suporte de memória secundária.
 ficheiros do tipo texto
Tipo de ficheiro 
 ficheiros do tipo binário (definidos pelo programador)
Ficheiros do tipo texto
Os ficheiros do tipo texto, são ficheiros em que a informação é totalmente
armazenada em formato de caracteres (ASCII), podendo ser criados ou modificados
por um editor de texto, fora do programa que os utiliza.
O acesso aos dados neste tipo de ficheiros é do tipo sequencial, querendo isto
dizer, que a leitura dos dados tem de partir sempre do início e percorrer todos os
elementos até ao ponto pretendido.
Ficheiros do tipo binário ou definidos pelo programador
Os ficheiros definidos pelo programador são ficheiros que agrupam dados
simples (como por exemplo, um inteiro) ou estruturados (como por exemplo, um
registo), em formato binário, assumindo formas muito variáveis. Podemos, por
exemplo, ter ficheiros de números inteiros ou reais, de vectores ou de registos, etc. o que permite uma grande flexibilidade de trabalho com sequências de dados
armazenadas em suportes de memória secundária.
Em particular, os ficheiros de registos permitem manipular dados num formato
bem estruturado para trabalho com informação externa (em disco ou disquete). Com
este tipo de ficheiros (ficheiros de registos) pode trabalhar-se com informação um
pouco à maneira de bases de dados.
O acesso aos dados destes ficheiros pode ser feito de forma aleatória, ou seja,
por escolha da posição pretendida, e não forçosamente de forma sequencial, como
nos ficheiros de texto.
Em qualquer dos casos, um ficheiro de dados (Ficheiro de texto ou binário), para
além de permitir o armazenamento da informação num suporte de armazenamento
externo à memória primária, também permite reunir uma colecção de dados sem
tamanho fixo à partida. Qualquer um dos outros tipos de dados estruturados
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
47
Unidade II
Estruturas de Dados
(vectores, matrizes ou registos) tem um tamanho - em termos de número de
elementos - que é determinado à partida, na respectiva declaração do tipo ou da
variável. Em princípio, um ficheiro não tem essa limitação, pois pode conter um
número maior ou menor de dados, sem que esse número tenha que ser determinado
à partida. Um ficheiro pode ser acrescentado com mais dados, desde que estes
sejam compatíveis com o tipo-base dos dados que o constituem, ou pode ser
totalmente reescrito com um número completamente diferente de elementos.
Operações com ficheiros
Os ficheiros são estruturas de dados que implicam a sua manipulação a dois
níveis:
1) ao nível interno do programa, com variáveis que identificam os ficheiros e os
dados a ler ou escrever nessas unidade de informação;
2) ao nível externo ou de interacção entre o programa e os dispositivos físicos
onde são armazenados os ditos ficheiros.
As principais operações a ter em conta no trabalho com ficheiros são:
•
declaração de tipos e variáveis de ficheiros;
•
associação de uma variável de ficheiro com um nome externo de ficheiro;
•
criação de novos ficheiros ou reescrita total de um ficheiro já existente;
•
escrita de informação num ficheiro;
•
abertura de um ficheiro para leitura;
•
procura de dados num ficheiro;
•
fecho de um ficheiro aberto;
•
fusão de ficheiros;
•
etc.
Exemplos de operações no trabalho com ficheiros de registo:
•
consultar registo por registo;
•
listar todos os registos de um ficheiro;
•
acrescentar mais registos;
•
alterar os dados de um determinado registo;
•
consulta de uma registo dada a sua posição no ficheiro
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
48
Unidade II
Estruturas de Dados
Instruções em pseudocódigo para manipulação de ficheiros
Como exemplo vamos usar um ficheiro chamado NOMES.DAT, cujos registos
contem 4 campos com o NOME, MORADA, IDADE e TELEFONE dos alunos da
turma. Algumas instruções usadas em pseudocódigo para manipulação de ficheiros
são as seguintes:
1. Em primeiro lugar é preciso declarar o ficheiro, o seu nome e a estrutura dos
registos:
NOMES.DAT é ficheiro de ALUNO
ALUNO é um registo composto por
NOME – alfanumérica
IDADE – numérica, inteira
MORADA – alfanumérica
TELEFONE – alfanumérica
Fim de registo
2. Abrir um ficheiro para escrita
Abrir NOMES.DAT para escrita
3. Escrita de um registo
Escrever NOMES.DAT, ALUNO
4. Abrir um ficheiro para leitura
Abrir NOMES.DAT para leitura
5. Testar fim de ficheiro
ff (NOMES.DAT)
6. Leitura de um registo
Ler ALUNO, NOMES.DAT
7. Fechar ficheiro
Fechar NOMES.DAT
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
49
Unidade II
Estruturas de Dados
Suportes físicos de ficheiros
As memórias auxiliares, embora sendo exteriores ao computador, operam como
extensões da sua memória central. São meios de memorização capazes de
armazenar quantidades de informação muito superiores às que a memória central
pode guardar, e funcionam como armazéns de informação que a memória central
requisita sempre que necessário. Existem grande variedade de memórias auxiliares,
como suporte físico de ficheiros, sendo mais frequentes as unidades de discos
magnéticos e as unidades de fita magnética.
Suportes físicos mais utilizados para armazenar informação:
•
banda magnética;
•
disquetes;
•
disco magnético duro;
•
discos ópticos;
•
tapes;
•
etc.
A escolha dos suportes relaciona-se com três características:
1. capacidade - sendo as bandas magnéticas, discos duro e óptico e as tapes,
de maior capacidade;
2. tipo de tratamento a dar ao ficheiro;
3. velocidade de tratamento - em que os discos duros e ópticos são os mais
rápidos.
O acesso aos ficheiros nestes suporte é também de grande importância pois
condiciona algumas das características atrás referidas. Assim o acesso pode ser de
dois tipos:
•
acesso sequencial;
•
acesso directo - nunca se poderia utilizar a banda magnética.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
50
Unidade II
Estruturas de Dados
3.9. Árvores Binárias
Conceitos Básicos
Uma árvore binária é o conjunto finito de elementos que: ou está vazio ou
contém um elemento chamado raiz da árvore, sendo os restantes elementos
divididos em dois subconjuntos disjuntos, cada um dos quais é, por si mesmo, uma
árvore binária.
A
B
C
D
E
G
F
H
I
Se n1 for a raiz de uma árvore binária e n2 for a raiz da sua sub-árvore direita ou
esquerda, então diz-se que n1 é o pai de n2, e que n2 é o filho direito ou esquerdo
de n1. O nó n2 é um descendente de n1 e n1 é o seu ascendente.
Um nó que não tenha filhos é uma folha. Dois nós são irmãos se são os filhos
direito e esquerdo do mesmo pai.
Nível de um nó numa árvore binária: a raiz da árvore tem nível 0. O nível de
outro nó qualquer é uma unidade mais que o nível do pai. Uma árvore binária
completa de nível n é uma árvore em que cada nó de nível “n” é uma folha e em que
todos os nós de nível menor que “n” têm sub-árvores direita e esquerda não vazias.
Uma árvore binária completa de nível 3 seria:
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
51
Unidade II
Estruturas de Dados
Estrutura de uma árvore binária
Pode considerar-se que cada nó de uma árvore binária tem três campos. O
campo de informação, que armazena informação Info(nodo), e dois campos de
ponteiros Esq(nodo) e Dir(nodo) que referenciam as raízes das sub-árvores esquerda
e direita da árvore binária cuja raiz é nodo. Se uma sub-árvore está vazia o seu
ponteiro é nil.
A estrutura de dados em pascal, de uma árvore binária, poderia ser a seguinte:
Type NodoPtr = ^Nodo;
Nodo = Record
Info: TipoDados;
Esq: NodoPtr;
Dir: NodoPtr;
End;
Var Tree: NodoPtr;
Operações sobre uma árvore binária
Algumas operações dinâmicas sobre uma árvore binária:
MakeTree(x) – Cria uma nova árvore binária consistindo num nó apenas, com x
no seu campo de informação e fornece um ponteiro para esse nó. Um algoritmo
possível seria:
P ! CriarNodo
Info(p) ! x
Esq(p) ! nil
Dir(p) ! nil
MakeTree ! p
SetEsq(p, x) – Aceita um ponteiro p (p≠nil) para um nó de uma árvore binária sem
filho esquerdo, criando, no nodo p, um filho esquerdo novo, cujo campo de
informação é preenchido por x. Um algoritmo possível seria:
Se Esq(p) ≠ nil
Então Escreva(‘Inserção inválida’)
Senão q ! MakeTree(x)
Esq(p) ! q
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
52
Unidade II
Estruturas de Dados
SetDir(p, x) – Aceita um ponteiro p para um nó de uma árvore binária sem filho
direito, criando, no nodo p, um filho direito novo, cujo campo de informação é
preenchido por x.
Aplicações de árvores binárias
Uma árvore binária é uma estrutura de dados útil quando decisões com duas
alternativas são tomadas em cada ponto de um processo.
Por exemplo, determinar os número que estão duplicados numa lista de
números. Para se reduzir o número de comparações podemos usar uma árvore
binária.
O primeiro número lido é colocado num nó que passa a ser a raiz de uma árvore
binária com as sub-árvores esquerda e direita vazias.
Cada número sucessivo na lista é comparado ao número na raiz. Se for igual
existe um duplicado. Se for menor, o processo é repetido para a sub-árvore
esquerda, e se for maior o processo é repetido para a sub-árvore direita.
O processo continua até que outro duplicado seja encontrado ou até se chegar a
uma sub-árvore vazia. Se se chegar a uma sub-árvore vazia o número é colocado
num novo nó nessa posição na árvore.
Um algoritmo possível seria:
1. [Lê-se o primeiro número e insere-se numa árvore binária com um único nó.]
Ler(NUM)
TREE ! MakeTree(NUM)
2. [Comparar os números da entrada e percorrer a árvore]
Enquanto <existirem números na entrada>
Ler(NUM)
Q ! TREE
{São necessários dois ponteiros para navegação na árvore}
P ! TREE
Enquanto NUM ≠ Info(P) e Q ≠ NIL
{Continua até que um duplicado seja encontrado
ou até se chegar a uma sub-árvore vazia}
P!Q
Se NUM < Info(P)
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
53
Unidade II
Estruturas de Dados
Então Q ! Esq(P)
Senão Q ! Dir(P)
Fim Enquanto
{Fim da pesquisa}
Se NUM = Info(P)
{Duplicado encontrado?}
Então Escrever(NUM, ‘ está duplicado’)
Senão Se NUM < Info(P)
{Número duplicado}
{Colocar num novo nodo com o número da entrada}
Então SetEsq(P, NUM)
Senão SetDir(P, NUM)
Fim Enquanto
{Fim de número da entrada}
Exemplo do algoritmo:
Sequência de números da lista: 14, 15, 4, 9, 7, 18, 3, 5, 16, 4, 20, 17, 9, 14, 5
14
4
15
3
9
7
5
18
16
20
17
Nem sempre os nós de uma árvore binária contêm todos o mesmo tipo de
informação. Por exemplo para representar uma expressão binária com operadores
numéricos constantes, podemos usar uma árvore binária cujas folhas contêm
números, contendo os outros nós caracteres representando operadores. Para
representar este tipo de árvores em pascal podemos usar registos variáveis.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
54
Unidade II
Estruturas de Dados
Travessias de uma árvores binárias
Outra aplicação seria percorrer uma árvore binária, atravessando todos os seus
nós, e enumerando-os.
No caso de uma árvore não há uma ordem “natural” de percurso. Vamos definir
três métodos para percorrer a árvore. Os métodos são definidos recursivamente de
modo que percorrer uma árvore binária implicará percorrer a raiz e as suas
sub-árvores esquerda e direita.
Métodos:
•
Pré-ordem (ou em profundidade)
•
Em-ordem (ou da ordem simétrica)
•
Post-ordem
Método da pré-ordem
1. Visitar a raiz
2. Percorrer a sub-árvore esquerda em pré-ordem
3. Percorrer a sub-árvore direita em pré-ordem
Implementação recursiva: PréOrdem(p) – Imprime o conteúdo de uma
árvore binária em pré-ordem. p é um ponteiro para o nó raiz de uma árvore
binária.
Se p ≠ NIL
Então Escrever(Info(p))
PréOrdem(Esq(p))
PréOrdem(Dir(p))
Método da em-ordem
1. Percorrer a sub-árvore esquerda em em-ordem
2. Visitar a raiz
3. Percorrer a sub-árvore direita em em-ordem
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
55
Unidade II
Estruturas de Dados
Implementação recursiva: EmOrdem(p) – Imprime o conteúdo de uma
árvore binária em em-ordem. p é um ponteiro para o nó raiz de uma árvore
binária.
Se p ≠ NIL
Então EmOrdem(Esq(p))
Escrever(Info(p))
EmOrdem(Dir(p))
Método da post-ordem
1. Percorrer a sub-árvore esquerda em post-ordem
2. Percorrer a sub-árvore direita em post-ordem
3. Visitar a raiz
Implementação recursiva: PostOrdem(p) – Imprime o conteúdo de uma
árvore binária em post-ordem. p é um ponteiro para o nó raiz de uma árvore
binária.
Se p ≠ NIL
Então PostOrdem(Esq(p))
PostOrdem(Dir(p))
Escrever(Info(p))
Exemplos:
Pré-ordem: A B D G C E H I F
Em-ordem: D G B A H E I C F
A
Post-ordem: G D B H I E F C A
B
C
D
E
G
H
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
F
I
56
Unidade II
Estruturas de Dados
Muitos algoritmos ou processos que usam árvores binárias começam por
construir uma árvore binária, e depois percorrem-na. Suponha-se que, dada uma
lista de números, se pretende imprimir esses números por ordem crescente.
Á medida que os números são lidos são inseridos numa árvore binária. Os
números duplicados são também incluídos na árvore binária. Quando um numero é
menor que o número contido num nó, é inserido no ramo da esquerda. Se o número
é maior que o número contido no nó (ou igual) é inserido no ramo da direita.
Nesta árvore binária todos os nós na sub-árvore esquerda de um nó “n” são
menores que o conteúdo de “n”, e todos os nós na sub-árvore direita de um nó “n”
são maiores ou iguais que o conteúdo do nó “n”.
Se a árvore for percorrida no método em-ordem (esquerda, raiz, direita) os
números são impressos em ordem crescente.
Algumas funções recursivas
O processo recursivo é constituído por duas partes: a primeira constituindo o
caso de base e a segunda o caso recursivo.
•
ContaNós(p) – Contar o número de nós numa árvore p
Se p≠ NIL
Então ContaNós ! 0
{Caso base}
Senão ContaNós ! 1 + ContaNós(Esq(p)) + ContaNós(Dir(p)) {Caso recursivo}
•
ContaFolhas(p) – Contar o número de folhas numa árvore p
Se p≠ NIL
Então ContaFolhas ! 0
{Caso base}
Senão Se (Esq(p)=NIL) e (Dir(p)=NIL)
Então ContaFolhas ! 1
{Caso base}
Senão ContaFolhas ! ContaFolhas(Esq(p)) + ContaFolhas(Dir(p))
{Caso recursivo}
•
Altura(p) – Calcula a altura de numa árvore p
Se p≠ NIL
Então Altura ! 0
{Caso base}
Senão Altura ! 1 + Maior(Altura(Esq(p)), Altura(Dir(p))
{Caso recursivo}
Em que, a função Maior(a, b) devolve o maior número, a ou b.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
57
Unidade II
4.
Estruturas de Dados
MÉTODOS
DE
ORDENAÇÃO
E
PESQUISA
4.1. Pesquisa Linear num Vector não Ordenado
1ª Versão
Dado um vector K não ordenado com N (≥ 1) elementos, pesquisa se existe ou não no
vector K um elemento K[I], de índice L, com o valor X dado.
1. [Inicializações]
L!1
2. [Pesquisar o vector enquanto não achar]
Repita enquanto L ≤ N e K[L] ≠ X
L!L+1
3. [Verificar se a pesquisa foi com ou sem sucesso]
Se L = N+1
Então Escrever(‘Insucesso’)
Senão Escrever(‘Sucesso em ‘, L)
4. [Termina]
Saída
Esta versão é óptima, mas não ideal! A condição de controlo do ciclo é
simplificável com o uso de uma sentinela em N+1.
Nesta versão, existem duas condições diferentes que podem terminar o
processo de pesquisa: encontra-se um elemento X = K[L]; o fim da sequência (L=N)
é atingido.
Estes ciclos com duas condições de fim de repetição simplificam-se usando uma
sentinela K[N+1] = X, pois deixa de ser necessário verificar se se ultrapassou o fim
da sequência.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
58
Unidade II
Estruturas de Dados
2º Versão
Dado um vector K desordenado, com N elementos (N ≥ 1) e dimensionado de N+1, este
algoritmo pesquisa o vector para encontrar um dado valor X. O elemento K[N+1] actua
como sentinela e antes da pesquisa recebe o valor de x.
1. [Inicializações]
L!1
K[N+1] ! X
2. [Pesquisar no vector]
Repita enquanto K[L] ≠ X
L!L+1
3. [Verificar se a pesquisa foi com ou sem sucesso]
Se L = N+1
Então Escrever(‘Insucesso’)
Senão Escrever(‘Sucesso em ‘, L)
4. [Termina]
Saída
Neste algoritmo a pesquisa é sempre (...) “bem sucedida”, pois a sentinela é
K[N+1] = X; por isso o índice L nunca é testado, o que torna esta versão mais
eficiente que a anterior.
É a pesquisa ideal, para vectores desordenados e deve sempre ser a utilizada.
4.2. Pesquisa Linear num Vector Ordenada
Dado um vector K ordenado, com N elementos, K[1] < K[2] < ... < K[N], este algoritmo
pesquisa no vector um dado elemento X. Por conveniência e rapidez supõe-se existir um
elemento K[N+1] que actua como sentinela: K[N+1] = ∞ > X.
1. [Inicializações]
L!1
K[N+1] ! 9999999
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
59
Unidade II
Estruturas de Dados
2. [Pesquisar no vector]
Repita enquanto X > K[L]
L!L+1
3. [Verificar se a pesquisa foi com ou sem sucesso]
Se X = K[L]
Então Escrever(‘Sucesso em ‘, L)
Senão Escrever(‘Insucesso’)
4. [Termina]
Saída
Há métodos muito mais eficientes, de pesquisa em vectores ordenados, em que
o tempo de pesquisa é mais reduzido.
4.3. Pesquisa Binária
Dado um vector K de N elementos ordenado, por ordem crescente, pesquisa se X existe
em K.
1. [Inicialização dos limites inferiores, superiores e médio dos intervalos de pesquisa]
INF ! 1
SUP ! N
MED ! TRUNC((INF + SUP) / 2)
2. [Bissecta-se o intervalo de pesquisa enquanto este tiver elementos e não se encontrar
X]
Repita enquanto INF < SUP e X ≠ K[MED]
Se X < K[MED]
Então SUP ! MED –1
Senão INF ! MED +1
MED ! TRUNC((INF + SUP) / 2)
3. [Resultado da pesquisa]
Se X = K[MED]
Então Escrever(‘Pesquisa com sucesso: ‘, X, ‘ está na posição ‘, MED)
Senão Escrever(‘Insucesso’)
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
60
Unidade II
Estruturas de Dados
4. [Termina]
Saída
4.4. Fusão Simples
Fusão: combinação de dois ou mais vectores ordenados num só.
Dado dois vectores ordenados A e B com respectivamente a dimensão de N e M
elementos, este algoritmo faz a fusão destes vectores (intercala) e produz um vector ordenado
C, que contém N+M elementos. As variáveis I, J, K e L são usadas como índices dos vectores.
1. [Inicialização]
I!J!K!1
2. [Comparar elementos e escolher o menor]
Repita enquanto I ≤ N e J ≤ M
Se A[I] ≤ B[J]
Então C[K] ! A[I]
I!I+1
K!K+1
Senão C[K] ! B[J]
J!J+1
K!K+1
3. [Copiar os elementos não processados para o vector de saída]
Se I = N
Então Repita para L = J, J+1, ..., M
C[K] ! B[L]
K ! K +1
Senão Repita para L = I, I+1, ..., N
C[K] ! A[L]
K ! K +1
4. [Termina]
Saída
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
61
Unidade II
Estruturas de Dados
4.5. Ordenação por Inserção Simples
Dado um vector K de N elementos (N ≥ 2), é ordenado por ordem crescente.
1. [Ciclo que sucessivamente escolhe os elementos]
Repetir até ao passo 3 para
L = 2, ..., N
2. [Preparar a pesquisa da posição correcta para a inserção]
J ! L-1
X ! K[L]
{ Elemento a inserir }
3. [Ciclo que faz a pesquisa da posição]
Repita enquanto J > 0 e X < K[J]
K[J+1] ! K[J]
{ Deslocamento para arranjar vaga para inserção correcta }
J!J–1
K[J+1] ! X
4. [Terminar]
Saída
Há duas condições diferentes que podem terminar o processo de transposição
(ciclo ‘Repita enquanto’): encontrar-se um elemento K[J] ≤ X; o lado esquerdo da
sequência é atingida (J=0).
Exemplo:
K[1]
K[2]
K[3]
K[4]
K[5]
K[6]
44
55
11
22
66
33
44
55
11
22
66
33
(55 foi inserido na posição 2)
11
44
55
22
66
33
(11 foi inserido na posição 1)
11
22
44
55
66
33
(22 foi inserido na posição 2)
11
22
44
55
66
33
(66 foi inserido na posição 5)
11
22
33
44
55
66
(33 foi inserido na posição 3)
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
62
Unidade II
Estruturas de Dados
4.6. Ordenação por Selecção Simples
Dado um vector K de N elementos, é ordenado por ordem crescente.
1. [Executar as N-1 passagens para sucessivamente seleccionar o mínimo e fazer a sua
troca com o elemento de índice PASS]
Repetir até ao passo 4 para
PASS = 1, 2, ..., N-1
2. [Inicializar o índice mínimo]
MIN ! PASS
3. [Executar uma passagem e obter o elemento com menor valor]
Repita para L=PASS+1, PASS+2, ..., N
Se K[L] < K[MIN]
Então MIN ! L
4. [Troca de elementos: se encontrou um mínimo faz a troca]
Se MIN ≠ PASS
Então TEMP ! K[MIN]
K[MIN] ! K[PASS]
K[PASS] ! TEMP
5. [Terminar]
Saída
Este algoritmo selecciona dos N elementos de um vector K aquele com o menor
valor e troca-o com o primeiro elemento. De seguida selecciona dos N-1 elementos
do vector aquele com o menor valor e troca-o com o segundo elemento. O algoritmo
repete estas operações com os restantes N-2, depois N-3, até que só um resta (o
maior).
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
63
Unidade II
Estruturas de Dados
Exemplo:
K[1]
K[2]
K[3]
K[4]
K[5]
K[6]
44
55
11
22
66
33
Passagem nº 1:
11
55
44
22
66
33
(44 trocou com 11)
Passagem nº 2:
11
22
44
55
66
33
(55 trocou com 22)
Passagem nº 3:
11
22
33
55
66
44
(44 trocou com 33)
Passagem nº 4:
11
22
33
44
66
55
(55 trocou com 44)
Passagem nº 5:
11
22
33
44
55
66
(66 trocou com 55)
4.7. Ordenação por Troca Simples
Dado um vector K de N elementos, é ordenado por ordem crescente.
1. [Inicialização]
LIMITE ! N
TROCA ! 1
{ Número qualquer maior que zero, isto é, que torna a condição de
controlo do ciclo ‘Repita Enquanto’ verdadeira. }
2. [Passagens só enquanto houver trocas. Compara e troca. A variável ‘Troca’
armazena o índice do vector da última troca efectuada, durante o ciclo]
Repita enquanto TROCA > 0
TROCA ! 0
{ Inicialmente, nenhuma troca foi efectuada }
Repita para J=1, 2, ..., Limite-1
Se K[J] > K[J+1]
Então X ! K[J]
{ Troca dos elementos K[J]!" K[J+1] }
K[J] ! K[J+1]
K[J+1] ! X
TROCA ! J
{ Armazena a posição da troca }
Fim Repita
LIMITE ! TROCA
{ Os elementos acima de ‘Limite’ já estão ordenados.
Elementos de maior valor }
3. [Termina]
Saída
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
64
Unidade II
Estruturas de Dados
Quando uma passagem do ciclo ‘Repita para’ não tem trocas (TROCA = 0) o
algoritmo pode terminar, já que o vector K já está ordenado.
No fim de cada ciclo ‘Repita para’, deve-se memorizar a posição (valor do índice
do vector K) da última troca na variável Limite. Todos os pares de itens adjacentes
acima deste limite já estão ordenados, e por isso, nas passagens seguintes deve-se
terminar em limite.
Exemplo:
K[1]
K[2]
K[3]
K[4]
K[5]
K[6]
44
55
11
22
66
33
Passagem nº 1:
44
11
55
22
66
33
(55 trocou com 11)
Passagem nº 1:
44
11
22
55
66
33
(55 trocou com 22)
Passagem nº 1:
44
11
22
55
33
66
(66 trocou com 33)
Passagem nº 2:
11
44
22
55
33
66
(44 trocou com 11)
Passagem nº 2:
11
22
44
55
33
66
(44 trocou com 22)
Passagem nº 2:
11
22
44
33
55
66
(55 trocou com 33)
Passagem nº 3:
11
22
33
44
55
66
(44 trocou com 33)
Passagem nº 4:
11
22
33
44
55
66
(não houve troca e termina)
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
65
Unidade II
Estruturas de Dados
Na impossibilidade temporal de incluir organizadamente neste manual a
aplicação dos conceitos com respectivos exercícios, junta-se agora algumas
aplicações por forma a que o aluno possa testar alguns dos conceitos adquiridos.
EXERCÍCIOS
1. Durante a elaboração de um programa de um projecto de informatização de um
terminal rodoviário, surgiram as seguintes necessidades, no que se refere a
estrutura de suporte de dados:
• É necessário manter, em memória secundária, o número de chegadas de
autocarros nos dias úteis de uma semana (de 2ª a 6ª feira), para todas as
semanas de um ano (52 semanas).
• Temporariamente, durante a execução do programa, é necessário guardar,
em memória primária, os totais respeitantes ao número de chegadas de
autocarros, nos dias da semana (de 2ª a 6ª feira).
• Para futuro cálculo estatístico mensal é necessário registar em memória
secundária, para cada autocarro que passa pelo terminal rodoviário, o seu
número da semana ( de 1 a 52), dia da semana (de 1 a 5), hora e número
de passageiros.
Seleccione a estrutura de dados (vector, matriz ou ficheiro) que mais se
adequa a cada um dos casos anteriores, apresentando para os vectores e
matrizes as suas dimensões e para os ficheiros o nome dos seus campos.
2. Sugira uma estrutura de dados (vector, matriz ou ficheiro) adequada ao registo,
em memória secundária, do número de peças com e sem defeitos produzidas em
determinada data. Apresente, no caso da estrutura de dados escolhida ser um
vector ou matriz, a sua dimensão; no caso de ser um ficheiro, o nome dos campos
constituintes dos seus registos.
3. Transcreve, para a sua folha de prova, os “termos” adequados ao preenchimento
dos espaços assinalados no algoritmo seguinte (#, $, %, &, ', ().
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
66
Unidade II
Estruturas de Dados
Este algoritmo determina a taxa de retenção de IRS na fonte, com base na
remuneração mensal do funcionário e no seu número de dependentes.
Considera-se a tabela de trabalho dependente casado único titular do IRS
constituída por 31 intervalos de remuneração, para determinar a respectiva taxa.
O vector INTERVALOS, com 31 elementos, contém os intervalos de remuneração
mensal da tabela. A matriz TAXAS, com 31 linhas e 6 colunas, contém as taxas
de retenção aplicáveis mediante a remuneração auferida e o número de
dependentes (0, 1, 2, 3, 4, 5 ou mais). Consideram-se ambas as estruturas
devidamente preenchidas.
1. [ Ler a remuneração mensal e o número de dependentes do funcionário]
Leia(REMUNERAÇÃO)
Leia(N_DEPENDENTES)
2. [Determinar o índice do intervalo de remuneração]
I!1
Repita enquanto # ______________________
$ __________ ! I + 1
3. [“Corrigir”, se necessário, o número de dependentes]
Se N_DEPENDENTES % _____________
Então J ! & _______________
Senão ' ___________________
4. [Imprimir a taxa de retenção adequada ao funcionário]
( _____________________________________________
5. [Terminar]
Saída
4. Dado dois vectores ordenados A e B contendo N e M elementos,
respectivamente, dispostos por ordem crescente, elabora um algoritmo que faz a
fusão destes vectores e produz um vector C ordenado, contendo todos os
elementos pares.
5. Dado dois vectores ordenados A e B, dispostos por ordem crescente, contendo N
e M elementos, respectivamente, e M maior que N, elabora um algoritmo para
verificar se A está contido em B. Nota: Procura tirar partido do ordenamento dos
elementos.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
67
Unidade II
Estruturas de Dados
6. Elabora uma função que devolve o número de elementos negativos de um vector
K não ordenado de N elementos.
7. Completa o passo 1 e 2 do algoritmo seguinte, de forma que seja emitida a
mensagem “Tem direito a bónus”, se o funcionário respeitar os seguintes
requisitos:
•
a totalidade dos dias de falta, desde o início do ano, não for superior a um dia
por mês decorrido, incluindo o mês a que se refere a remuneração (MÊS).
Assuma que o vector FALTAS está preenchido com o número de dias de falta,
em cada mês, do funcionário;
•
ÍNDICE de produtividade for superior à produtividade média (PRODMÉDIA).
Considere que a produtividade média já foi calculada e atribuída à variável
respectiva;
•
a CLASSIFICAÇÃO da qualidade do trabalho desenvolvido for “Bom”.
1. [Ler o mês, o Índice e a classificação]
2. [Avaliação dos requisitos de atribuição de bónus]
8.
Elabora o passo 2 do algoritmo seguinte, de forma a que seja calculada e
impressa a importância a pagar pelo cliente relativamente ao tempo de aluguer de
um vídeo. O valor a cobrar por dia é calculado da seguinte maneira:
•
no primeiro e segundo dia o aluguer do vídeo é pago a 400 Esc.; no terceiro
dia o aluguer do vídeo é pago a 500 Esc.; nos restantes dias o aluguer do
vídeo é pago com um acréscimo de 200 Esc. ao valor do dia anterior, ou seja,
no quarto é pago a 700 Esc., no quinto dia a 900 Esc., etc.
1. [Ler o número de dias de permanência]
Leia(DIAS)
2. [Calcular e imprimir a importância a pagar]
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
68
Unidade II
Estruturas de Dados
9. Suponha a adivinha do caracol que se encontra no fundo dum poço de 10
metros, subindo cada dia 3 metros, mas descendo depois 2. Quantos dias demora o
caracol a chegar ao topo?
Elabora um algoritmo, que através duma pilha, calcule a adivinha. Cada metro
deve representar um elemento da pilha. A pilha deve ser representada usando um
vector.
O trabalho deve ser realizado individualmente ou em grupo de dois elementos,
em pascal ou C. Todos os trabalhos apresentarão uma versão fonte devidamente
comentada e uma versão executável.
O objectivo fundamental deste trabalho é a capacidade de aplicar os conceitos
teóricos na resolução de questões de natureza prática.
Os trabalhos deverão ser entregues até dia 24 de Outubro, caso contrário a sua
avaliação será penalizada.
ESEV(Prof. Serv.) - Prof. José Alberto Pereira
69
Download