Ementa:
1. Introdução.
2. Tipos de Dados e Abstração.
3. Matrizes.
4. Listas.
5. Pilhas e Filas.
6. Árvores.
7. Grafos.
8. Ordenação e Técnicas de Busca.
Bibliografia Básica:
Bibliografia Complementar:
Download:
1. INTRODUÇÃO
1.1.1. Pseudolinguagem
• Tipos de Dados Básicos: todo valor (constante ou variável) de um programa tem um tipo
associado que determina o conjunto de valores que podem ser assumidos e o conjunto de
operações a que pode ser submetido. Existem quatro tipos básicos:
- Tipo inteiro ou int: Um objeto do tipo int pode assumir qualquer valor inteiro. As operações
possíveis são: +, -, *, /, div, e mod. Cada uma dessas operações recebe dois argumentos
inteiros e fornece um resultado inteiro, com exceção da divisão por / que oferece um
resultado real. Os seguintes operadores relacionais podem ser aplicados a dois argumentos
inteiros para compará-los <, ≤, =, >, ≥ e ≠, sendo que nessas operações o resultado é do tipo
lógico.
- Tipo real: Um objeto do tipo real pode assumir qualquer valor real. As operações
permissíveis sempre recebem dois argumentos reais e, de acordo com o resultado, são:
resultado real: +, -, *, / ;
resultado lógico: <, ≤, =, >, ≥ e ≠ .
- Tipo lógico ou log: Um objeto deste tipo assume um dos valores verdadeiro ou falso. As
operações aplicáveis a dois argumentos do tipo lógico são:
conectivos lógicos: conjunção (e, ^), disjunção (ou, ∨), disjunção exclusiva (xou, ⊕),
negação (não, ¬). A negação trabalha com somente um argumento.
conectivos relacionais: = e ≠.
Qualquer dessas operações fornece resultado do tipo log.
- Tipo caráter (ou caracter) ou car: Os dígitos decimais, as letras, e os caracteres especiais (+,
., /, ,, etc...) constituem os chamados caracteres alfanuméricos ou conjunto de valores
onde um objeto do tipo car pode assumir o seu valor. São exemplos de constantes do tipo
car: ´7´, ´R´, ´x´, ´*´. Dois argumentos do tipo car podem ser submetidos às operações de
comparação (<, ≤, =, >, ≥ e ≠ ), que fornecem resultado lógico.
• Declaração de variáveis:
- Sintaxe: tipo : nome (s);
- Implicações da declaração de uma variável:
alocação de um espaço na memória que possa conter um valor do seu tipo.
associação do endereço dessa posição da memória ao nome da variável.
A partir daí, toda vez que esta variável for referenciada em qualquer comando do programa,
o computador vai trabalhar com o conteúdo de seu endereço, que é o valor da variável.
3
• Comando de atribuição:
- Sintaxe: uma variável ← expressão;
- Semântica: atribuir à variável o resultado da expressão.
- Expressões aritméticas: fornecem resultado numérico (int ou real).
Operações básicas: +, -, *, /.
Exponenciação: ( A + B ) ** N
Funções matemáticas comuns: sen (X), cos (X), abs (X) (| X |), raiz (X), arctan (X),
exp (X) (eX), log (X), ln (X) etc.
Operadores para inteiros: M mod I resto (módulo) da divisão inteira de M por I
M div I quociente da divisão inteira de M por I
- Expressões lógicas: fornecem resultado lógico
Conectivos lógicos:
(e, ^) conjunção;
(ou, ∨) disjunção;
(xou, ⊕) disjunção exclusiva;
(não, ¬) negação.
Conectivos relacionais: (<, ≤, =, >, ≥ e ≠)
- Prioridade de execução das operações em uma expressão:
1º. Parênteses (dos mais internos para os mais externos);
2º. Expressões aritméticas:
1. funções
2. **
3. * e /
4. + e -
3º. Comparações: <, ≤, =, >, ≥ e ≠
4º. não
5º. e
6º. ou e xou
7º. Da esquerda para a direita quando houver indeterminações
• Comentários: {...}
• Pseudo-comandos: “ . . . ”;
Exemplo: “Calcular o valor da raiz da equação”;
• Estruturas de controle:
- Seqüência simples: conjunto de comandos, separados por ponto-e-vírgula (;), que serão
executados numa sequência linear de cima para baixo.
Sintaxe:
C1;
C2;
...
Cn;
Semântica: o controle de fluxo de execução entra na estrutura, executa comando por
comando, de cima para baixo e sai da estrutura.
4
- Alternativa: ocorre quando uma ação a ser executada depende de uma inspeção ou teste de
uma condição. Pode ser:
alternativa simples:
sintaxe: se <condição>
então
C;
fim-se;
alternativa dupla:
sintaxe:
se <condição>
então
C1 ;
senão
C2 ;
fim-se;
- Repetição:
Com teste no início:
sintaxe:
enquanto <condição> faça
C ;
fim-enquanto;
5
Ideal para programas ou trechos lógicos suficientemente simples que possam ser
expressados diretamente em pseudo-linguagem.
Este método é constituído dos seguintes passos:
1. Entender perfeitamente o problema a resolver.
6
início
definição de novos tipos;
declaração de variáveis;
inicialização de variáveis;
corpo do algoritmo controlando:
leitura;
processamento;
saída;
fim.
5. Testar o algoritmo, considerando se possível, todas as alternativas de entrada.
6. Traduzir o algoritmo para uma linguagem de programação obtendo um programa que
deve ainda ser testado exaustivamente no computador e finalmente documentado.
Aplicação 1:
Desenvolver um algoritmo para ler vários números inteiros, calcular e imprimir:
1. Se o número lido não for negativo: o número lido e o seu fatorial.
2. No final: a quantidade de números negativos lidos.
Obs.: FLAG = 999
Ideal para programas mais complexos, onde deve-se manter inicialmente um nível mais alto
de abstração, isto é, manter a atenção em o que fazer e não em como fazer.
Usando a técnica dos refinamentos sucessivos, dispensa-se o estudo de métodos e parte-se
logo para a elaboração do algoritmo sendo que cada tarefa lógica distinta será representada por uma
frase descritiva da ação correspondente (pseudo-comando).
A primeira versão do algoritmo é rapidamente obtida pela listagem de alguns poucos
pseudo-comandos que deverão ser detalhados em refinamentos posteriores.
O refinamento de um pseudo-comando pode ser feito em função de outros pseudo-comandos
ou em função da própria pseudo-linguagem quando a ação lógica correspondente for considerada
mais elementar.
Depois que todos os pseudo-comandos já estiverem expressos em termos da pseudo-
linguagem, pode-se juntar as partes montando a versão final e completa do algoritmo.
Os pseudo-comandos originais são geralmente mantidos no texto do algoritmo sob a forma
de comentários melhorando a sua inteligibilidade.
Na prática este é o método mais usado, onde os controles de caráter geral e as ações mais
simples são desenvolvidas diretamente e a técnica de refinamentos sucessivos fica somente para as
ações lógicas que envolvem uma maior complexidade.
Observação: Em qualquer dos métodos, pode-se desenvolver as ações lógicas bem definidas na
forma de procedimentos (ou funções) a serem referenciados (chamados) no texto do algoritmo, no
momento da execução da ação correspondente. A utilização destes módulos funcionais constitui
uma técnica (programação modular) que geralmente diminui o esforço do programador e aumenta a
qualidade de seus programas.
a) Procedimento
É um bloco precedido de um nome, podendo ter uma lista de parâmetros de entrada e/ou saída.
Por possuir nome, o procedimento pode ser referenciado (chamado para execução) em qualquer (um
ou mais) ponto do algoritmo.
- Sintaxe:
Procedimento <nome> (<lista de parâmetros formais>);
início
<declaração de variáveis locais >;
C1;
C2;
.
.
.
Cn;
fim; { nome }
- Forma de chamada:
<nome> (<lista de argumentos>);
- Observações:
As variáveis locais só estarão alocadas durante o tempo de execução do procedimento.
Os parâmetros formais são tratados como variáveis locais no procedimento, sendo que
um parâmetro formal de entrada assume seu valor na chamada do procedimento e um
parâmetro formal de saída tem seu valor final calculado dentro do procedimento
(passagem de parâmetros por referência).
Um argumento (parâmetro efetivo) de entrada pode ser uma constante, uma variável ou
uma expressão e um argumento de saída só pode ser uma variável.
A lista de parâmetros formais e a lista de argumentos têm que concordar em número,
ordem e tipo.
Aplicações:
2. Desenvolver um procedimento para dado um número inteiro não negativo, calcular o
seu fatorial.
3. Refazer a aplicação 1, substituindo o cálculo do fatorial pela chamada do procedimento
anterior.
b) Função
8
Sintaxe:
função <nome> (<lista de parâmetros formais>): <tipo da função>;
início
<declaração de variáveis locais>;
C1;
C2;
.
.
.
Cn;
<nome> ← <expressão>; {valor que será retornado no nome da}
{função. O tipo de dado da <expressão> deve ser o}
{mesmo do <tipo da função> }
fim; {nome}
Forma de chamada:
...<nome> (<lista de argumentos>); ... {dentro de uma
expressão }
Aplicações:
4. Desenvolver uma função para dado um número inteiro não negativo, calcular o seu
fatorial.
5. Refazer a aplicação 1, substituindo o cálculo do fatorial pela chamada da função
anterior.
Ex.:
9
Solução iterativa:
Solução recursiva:
1, se N = 0
Por definição: N!=
N * (N – 1) !, se N > 0
Solução iterativa:
10
onde seu único objetivo é chamar o procedimento SORT1. Portanto, pode-se ignorar
este procedimento SORT, iniciando o processo pela chamada: SORT1 (1, N, A);.
Aplicação 6:
Um programa pode ser analisado sob vários aspectos: atinge os objetivos propostos, está
correto (corretude), possui boa documentação, possui boa estrutura lógica dos módulos funcionais,
facilidade de alterações e manutenção, o código está inteligível, amigabilidade e funcionalidade
operacional, possui bom desempenho etc.
Embora todos os aspectos sejam igualmente importantes, destaca-se exclusivamente a
análise relativa ao desempenho do programa, que trata da medida do tempo de computação e da
ocupação de espaço de memória. Com o grande desenvolvimento tecnológico do hardware, a
memória vem oferecendo cada vez mais capacidade de armazenamento a um custo cada vez menor,
tornando a questão de ocupação de espaço de memória pouco relevante. Por isso, nesta análise será
dada ênfase somente à medida do tempo de computação.
Tempo de execução da instrução i é sempre de difícil obtenção por causa de fatores como:
• a tradução da linguagem fonte para a de máquina feita por um compilador específico;
• conjunto das instruções da linguagem de máquina;
• tempo gasto por cada instrução de máquina;
• a máquina e os recursos (rede, sistema operacional etc.) usados;
• etc.
A freqüência pode variar de um conjunto de dados para outro. Por isso, deve-se estar atento
na escolha adequada do conjunto de dados para efeito desta contagem.
Se a instrução considerada é a que possui maior freqüência no programa, então ela passa a
ser chamada de ordem de grandeza de crescimento de tempo do programa. A ordem de grandeza
de um algoritmo é o principal parâmetro para análise de sua execução, sendo a sua determinação
obtida geralmente por fórmulas como:
• 1 O(1)
N
• ∑1 = N
i =1
O(N)
N
• ∑ i = [ N ( N + 1 ) ] / 2 = N2 / 2 + termos de grau inferior
i =1
O ( N2 )
14
N
• ∑i 2
= [ N ( N + 1 ) ( 2N + 1 ) ] / 6 = N3 / 3 + termos de grau inferior O ( N3 )
i =1
• ... ...
N
• ∑i K
= NK + 1 / (K + 1) + termos de grau inferior, K ≥ 0 O ( NK + 1 )
i =1
Quando se diz que um programa possui O ( NK + 1 ) isto significa que o seu crescimento de
tempo de execução é proporcional a NK + 1.
Exemplo: Analisar a solução iterativa de um algoritmo que leia um valor inteiro N, calcule e
imprima o seu fatorial. Se o valor lido para N for negativo, imprimir uma mensagem de erro.
início
int: N, FAT, C;
(1) leia (N);
(2) se N < 0 então
(3) imprima (N, ‘NEGATIVO’);
senão
(4) FAT ← 1;
(5) para C de 1 até N faça
(6) FAT ← FAT * C;
fim-para
(7) imprima (FAT);
fim-se;
fim.
N 10 N N2 / 2
1 10 0,5
15
5 50 12,5
10 100 50
15 150 112,5
20 200 200
25 250 312,5
30 300 450
35 350 612,5
. . .
. . .
. . .
600
400
Tempo
200
0
1 5 10 15 20 25 30 35
N 10 N
N2 / 2
Obs.:
• O parâmetro N caracteriza a quantidade de entradas, a quantidade de saídas, a soma
dessas quantidades ou a grandeza de uma delas.
• As ordem de grandeza mais comuns nos algoritmos são:
O (1) → tempo de computação constante
O (log 2N) → logaritmo de base 2
O (N) → linear
O (N log 2N)
O (N 2) → quadrática
O (N 3) → cúbica
O (2 N) → exponencial
1500
2N
1000
Tempo
500
N3
N2
0
0 2 4 6 8 10
N
log 2N N N log 2N N2 N3 2N
0 1 0 1 1 2
1 2 2 4 8 4
2 4 8 16 64 16
3 8 24 64 512 256
4 16 64 256 4096 65536
5 32 160 1024 32768 4,29496 x 10 9