Explorar E-books
Categorias
Explorar Audiolivros
Categorias
Explorar Revistas
Categorias
Explorar Documentos
Categorias
1
Prof: Jos Matias Pedro
1. Introduo
O curso de Estruturas de Dados discute diversas tcnicas de programao,
apresentando as estruturas de dados bsicas utilizadas no desenvolvimento de
software. O curso tambm introduz os conceitos bsicos da linguagem de
programao C, que utilizada para a implementao das estruturas de dados
apresentadas.
A linguagem de programao C tem sido amplamente utilizada na elaborao de
programas e sistemas nas diversas reas em que a informtica atua, e seu
aprendizado tornou-se indispensvel tanto para programadores profissionais como
para programadores que atuam na rea de pesquisa.
O conhecimento de linguagens de programao por si s no capacita
programadores necessrio saber us-las de maneira eficiente.
O projeto de um programa engloba a fase de identificao das propriedades dos
dados e caractersticas funcionais. Uma representao adequada dos dados, tendo
em vista as funcionalidades que devem ser atendidas, constitui uma etapa
fundamental para a obteno de programas eficientes e confiveis.
A linguagem C, assim como as linguagens Fortran e Pascal, so ditas linguagens
convencionais, projetadas a partir dos elementos fundamentais da arquitetura de
von Neuman, que serve como base para praticamente todos os computadores em
uso. Para programar em uma linguagem convencional, precisamos de alguma
maneira especificar as reas de memria em que os dados com que queremos
trabalhar esto armazenados e, freqentemente, considerar os endereos de
memria em que os dados se situam, o que faz com que o processo de programao
envolva detalhes adicionais, que podem ser ignorados quando se programa em uma
linguagem como Scheme. Em compensao, temos um maior controle da mquina
quando utilizamos uma linguagem convencional, e podemos fazer programas
melhores, ou seja, menores e mais rpidos.
A linguagem C prov as construes fundamentais de fluxo de controle necessrias
para programas bem estruturados: agrupamentos de comandos; tomadas de deciso
(if-else); laos com testes de encerramento no incio (while, for) ou no fim (do-while); e
seleo de um dentre um conjunto de possveis casos (switch). C oferece ainda
acesso a apontadores e a habilidade de fazer aritmtica com endereos. Por outro
lado, a linguagem C no prov operaes para manipular diretamente objetos
compostos, tais como cadeias de caracteres, nem facilidades de entrada e sada: no
h comandos READ e WRITE. Todos esses mecanismos devem ser fornecidos por
funes explicitamente chamadas. Embora a falta de algumas dessas facilidades
possa parecer uma deficincia grave (deve-se, por exemplo, chamar uma funo para
comparar duas cadeias de caracteres), a manuteno da linguagem em termos
modestos tem trazido benefcios reais. C uma linguagem relativamente pequena e,
no entanto, tornou-se altamente poderosa e eficiente.
2
Prof: Jos Matias Pedro
PS IM
Programa Fonte Interpretador Saida
Dados de Entrada
3
Prof: Jos Matias Pedro
PC CM PM
Execuo
PM Saida
Dados de Entrada
Programa Objecto
Devemos notar que, na 1 Figura ( Execuo de programa com linguagem Interpretada), o
programa fonte um dado de entrada a mais para o interpretador. No caso da
compilao, na 2 Figura (Execuo de um programa com linguagem compilada), identificamos
duas fases: na primeira, o programa objeto a sada do programa compilador e, na
segunda, o programa objeto executado, recebendo os dados de entrada e gerando
a sada correspondente.
O que Lgica?
a arte de pensar corretamente e, visto que a forma mais complexa do pensamento
o raciocnio, a Lgica estuda ou tem em vista a correo do raciocnio.
Podemos ainda dizer que a lgica tem em vista a ordem da razo. Isto d a entender
que a nossa razo pode funcionar desordenadamente, pode pr as coisas de pernas
para o ar. Por isso a Lgica ensina a colocar Ordem no Pensamento.
Exemplo:
a) Todo o mamfero animal.
Todo cavalo mamfero.
Portanto, todo cavalo animal.
b) Todo mamfero bebe leite.
O homem bebe leite.
Portanto, todo homem mamfero e
animal.
O que Algoritmo?
Algoritmo uma seqncia de passos que visam atingir um objetivo bem definido. Ou
ainda podemos definir, um algoritmo como uma sequncia extremamente precisa de
instrues que, quando lida e executada por uma outra pessoa, produz o resultado
esperado, isto , a soluo de um problema. Esta sequncia de instrues nada
mais nada menos que um registro escrito da sequncia de passos necessarios que
devem ser executados para manipular informaes, ou dados, para se chegar na
resposta do problema.
Em geral um algoritmo destina-se a resolver um problema: fixa um padro de
comportamento a ser seguido, uma norma de execuo a ser trilhada, com o objetivo
de alcanar a soluo de um problema.
4
Prof: Jos Matias Pedro
at obter um creme homogneo. Adicione o leite aos poucos. Desligue a batedeira e adicione
a farinha de trigo, o chocolate em p, o fermento e reserve. Bata as claras em neve e junte-
as massa de chocolate misturando delicadamente. Unte uma forma retangular pequena
com manteiga e farinha e leve para assar em forno mdio pr-aquecido por aproximadamente
30 minutos. Desenforme o bolo ainda quente e reserve.
Este um bom exemplo de algoritmo pois podemos extrair caracteristicas bastante
interessantes do texto. Em primeiro lugar, a pessoa que escreveu a receita no
necessariamente a mesma pessoa que vai fazer o bolo. Logo, podemos estabelecer,
sem prejuzo, que foi escrita por um mas ser executada por outro.
Outras caractersticas interessantes que esto implicitas so as seguintes:
_ as frases so instrues no modo imperativo: bata isso, unte aquilo. So ordens,
no sugestes. Quem segue uma receita obedece quem a escreveu;
_ as instrues esto na forma sequencial: apenas uma pessoa executa. No existem
aes simultneas.
_ existe uma ordem para se executar as instrues: primeiro bata a manteiga e
o aucar; depois junte as gemas, uma a uma, at acabar os ovos; em seguida
adicione o leite.
_ algumas instrues no so executadas imediatamente, preciso entrar em um
modo de repetio de um conjunto de outras instrues: enquanto houver ovos no
usados, junte mais uma gema. S pare quando tiver usado todos os ovos.
_ algumas outras instrues no foram mencionadas, mas so obviamente necess
arias que ocorram: preciso separar as gemas das claras antes de comear a tarefa
de se fazer o bolo, assim como preciso ainda antes quebrar os ovos.
_ algumas instrues, ou conjunto de instrues, podem ter a ordem invertida: pode-
se fazer primeiro a massa e depois a cobertura, ou vice-e-versa. Mas nunca
se pode colocar no forno a assadeira antes de se chegar ao trmino do preparo
da massa.
5
Prof: Jos Matias Pedro
O que um programa?
Para que um computador possa desempenhar uma tarefa necessrio que esta seja
detalhada passo-a-passo, numa forma compreensvel pela mquina, utilizando aquilo
que se chama de programa. Neste sentido, um programa de computador nada mais
que um algoritmo escrito numa forma compreensvel pelo computador (linguagem
de programao). Ou ainda, um programa a codificao em alguma linguagem
formal que garanta que os passos do algoritmo sejam executados da maneira como
se espera por quem executa as instrues.
E assim tambm com os algoritmos escritos para computador, voc deve especificar
todos os passos, para que o computador possa chegar ao objetivo.
Exemplo1:
Dados os nmeros naturais(N)
0, 1, 2, 3, 4, 5, 6, ...
passo1: faa N igual a zero
passo2: some 1 a N
passo3: volte ao passo 2
Exemplo2:
Soma dos primeiros 100 nmeros naturais:
passo1: faa N igual a zero
passo2: some 1 a N
passo3: se N for menor ou igual a 100
ento volte ao passo 2
seno pare.
Nos dois exemplos acima, o primeiro possui repertrio bem definido, mas no finito,
enquanto que o segundo tem um critrio de parada, ou seja, finito e descreve um
padro de comportamento, ou seja, temos um algoritmo.
Etapas de um programa
Ao montar um algoritmo, precisamos primeiro dividir o problema apresentado em
trs fases fundamentais, como se conceitua Processamento de Dados:
6
Prof: Jos Matias Pedro
Descrio Narrativa
Nesta forma de representao os algoritmos so expressos diretamente em
linguagem natural.
Como exemplo tem-se os algoritmo seguinte:
Troca de um pneu furado:
Afrouxar ligeiramente as porcas
Suspender o carro
Retirar as porcas e o pneu
Colocar o pneu reserva
Apertar as porcas
Abaixar o carro
Dar o aperto final nas porcas
Esta representao pouco usada na prtica porque o uso da linguagem natural
muitas vezes d oportunidade a ms interpretaes, ambigidades e imprecises.
Vantagens:
- O portugus bastante conhecido por ns;
Desvantagens:
- Impreciso;
- Pouca confiabilidade (a impreciso acarreta a desconfiana);
- Extenso (normalmente, escreve-se muito para dizer pouca coisa).
Fluxograma
7
Prof: Jos Matias Pedro
Inicio
N1
N2
Media ((N1+N2)/2)
8
Prof: Jos Matias Pedro
Media>=7
Reprovado Aprovado
Fim
Exemplo de um fluxograma convencional
Pseudocdigo
De forma semelhante como os programas so escritos. Tambm chamado de
Portugol ou Portugus Estruturado.
A linguagem de Programao mais prxima o Pascal, com codificao quase
idntica ao Ingls Estruturado.
Esta forma de representao de algoritmos rica em detalhes, como a definio dos
tipos das variveis usadas no algoritmo. Por assemelhar-se bastante forma em que
os programas so escritos, encontra muita aceitao.
A forma geral da representao de um algoritmo na forma de pseudocdigo a
seguinte:
algoritmo <Nome_do_Programa>
<declarao_de_variveis>
inicio
<corpo do algoritmo>
FimAlgoritmo
Algoritmo uma palavra que indica o incio da definio de um algoritmo em Portugol
ou Pseudocdigo.
<Nome_do_Programa> um nome simblico dado ao algoritmo com a finalidade de
distingui-los dos demais.
9
Prof: Jos Matias Pedro
AlgoritmoCalculoMedia:
var n1, n2, media: real
inicio
leia n1
leia n2
media ((n1 + n2) / 2)
se (media >= 7) entao
escreva (aprovado)
senao
escreva (reprovado)
fimse
FimAlgoritmo
10
Prof: Jos Matias Pedro
Fluxograma:
INICIO
N1
N2
SOMA N1+N2
SOMA
FIM
Portugol:
algoritmo Algoritmo01
var
N1 : real
N2 : real
SOMA : real
inicio
leia N1
leia N2
SOMA <- N1 + N2
escreva SOMA
FimAlgoritmo
Deciso Simples
Se <condio> entao
{bloco verdade}
Fimse
<condio> uma expresso lgica, que, quando inspecionada, pode gerar um
resultado falso ou verdadeiro.
Se .V., a ao primitiva sob a clusula ser executada; caso contrrio, encerra o
comando, neste caso, sem executar nenhum comando.
Algoritmo - 02: Ler dois valores numricos, efetuar a adio e apresentar o
resultado caso o valor somado seja maior que 10.
Planejamento
11
Prof: Jos Matias Pedro
Deciso Composta
Se <condio> entao
{bloco verdade}
Senao
{bloco falso}
Fimse
<condio> uma expresso lgica, que, quando inspecionada, pode gerar um
resultado falso ou verdadeiro.
Algoritmo - 03: Ler dois valores numricos, efetuar a adio. Caso o valor
somado seja maior ou igual a 10, este dever ser apresentado somando-se a
ele mais 5, caso o valor somado no seja maior ou igual a 10, este dever ser
apresentado subtraindo-se 7.
Planejamento
Problema: Calcular a soma de dois nmeros e apresentar o resultado com uma
condio.
Objetivo: Apresentar o Resultado se for igual ou maior que 10 adicionando mais 5,
se no for igual ou maior que 10 dever subtrair 7.
Entradas: A, B
Sadas: TOTAL
Auxiliares: SOMA
Projeto
Clculos e detalhamento:
SOMA <- A + B
Se (SOMA >= 10) Ento
TOTAL <- SOMA + 5
12
Prof: Jos Matias Pedro
Seno
TOTAL <- SOMA - 7
Fimse
Portugol
Algoritmo Algoritmo03
Var
A: Real
B: Real
SOMA: Real
TOTAL: Real
Inicio
Leia A
Leia B
SOMA A + B
Se (SOMA >= 10) entao
TOTAL SOMA + 5
Senao
TOTAL SOMA 7
Fimse
Escreva TOTAL
FimAlgoritmo
Seleo Mltipla
Esta estrutura evita que faamos muitos blocos se, quando o teste ser sempre em
cima da mesma varivel.
Exemplo:
Se (variavel = valor1) entao
comando1
Senao
Se (variavel = valor2) entao
Comando2
Senao
Se (variavel = valor3) entao
Comando3
Senao
Se (variavel = valor4) entao
Comando4
Senao
comando5
Fimse
Fimse
Fimse
Fimse
Com a estrutura de escolha mltipla, o algoritmo ficaria da seguinte maneira:
escolha variavel
caso valor1
comando1
caso valor2
comando2
caso valor3
13
Prof: Jos Matias Pedro
comando3
caso valor4
comando4
outrocaso
comando5
fimescolha
Estruturas de Repetio
Estas estruturas possibilitam que nosso algoritmo seja muito mais enxuto e fcil de
se programar.
Imagine um algoritmo de fatorial de 8:
variaveis
fat : real;
incio
fat <- 8 * 7
fat <- fat * 6
fat <- fat * 5
fat <- fat * 4
fat <- fat * 3
fat <- fat * 2
escreva (fat)
FimAlgoritmo.
O resultado ser o fatorial com certeza, mas, imagine se fosse o fatorial de 250. Ou
ainda, o usurio deseja fornecer o nmero e o algoritmo deve retornar o fatorial, qual
nmero ser digitado?
Quantas linhas devero ser escritas?
Para isso servem as estruturas de repetio, elas permitem que um determinado
bloco de comandos seja repetido vrias vezes, at que uma condio determinada
seja satisfeita.
Sero estudadas as seguintes estruturas de repetio:
- Enquanto - Faca, o teste realizado no Incio do looping
- Repita - Ateque, o teste realizado no Final do looping
- Para - de - ate - Passo - faca, cuja Estrutura utilizada uma varivel de Controle.
Para ... de ... ate ... Passo ... faca Estrutura com varivel de Controle
Nas estruturas de repetio vistas at agora, acorrem casos em que se torna difcil
determinar quantas vezes o bloco ser executado. Sabemos que ele ser executado
enquanto uma condio for satisfeita - enquanto..faa, ou at que uma condio seja
satisfeita - repita...at. A estrutura para .. passo repete a execuo do bloco um
nmero definido de vezes, pois ela possui limites fixos:
Para <varivel> <- <valor> at <valor> passo N faca
14
Prof: Jos Matias Pedro
Vetor
O vetor uma estrutura de dados linear que necessita de somente um ndice para
que seus elementos sejam endereados. utilizado para armazenar uma lista de
valores do mesmo tipo, ou seja, o tipo vetor permite armazenar mais de um valor em
uma mesma varivel. Um dado vetor definido como tendo um nmero fixo de clulas
idnticas (seu contedo dividido em posies). Cada clula armazena um e
somente um dos valores de dados do vetor. Cada uma das clulas de um vetor possui
seu prprio endereo, ou ndice, atravs do qual pode ser referenciada. Nessa
estrutura todos os elementos so do mesmo tipo, e cada um pode receber um valor
diferente [ 3, 21, 4].
Algumas caractersticas do tipo vetor([10]):
Alocao esttica (deve-se conhecer as dimenses da estrutura no momento
da declarao)
Estrutura homognea
Alocao seqencial (bytes contguos)
Insero/Excluso
o Realocao dos elementos
o Posio de memria no liberada
Exemplo de um vetor
Vetor
Nota 9.5 7.4 8.1 4.6 4.5
1 2 3 4 5 ndices.
A figura acima mostra um vetor de notas de alunos, a referncia NOTA[4] indica o valor 4.6 que se
encontra na coluna indicada pelo ndice 4.
15
Prof: Jos Matias Pedro
char c[4];
c[0]=a;
c[1]=b;
c[ 2]=c;
c[ 3]=d;
Lembrete: Caso seja colocada num programa a instruo a [2]++ est sendo dito
que a posio do vetor a ser incrementada.
2.1.2- Matriz
Uma matriz um arranjo bidimensional ou multidimensional de alocao esttica e
seqencial. A matriz uma estrutura de dados que necessita de um ndice para
referenciar a linha e outro para referenciar a coluna para que seus elementos sejam
endereados. Da mesma forma que um vetor, uma matriz definida com um tamanho
fixo, todos os elementos so do mesmo tipo, cada clula contm somente um valor e
os tamanhos dos valores so os mesmos (em C, um char ocupa 1 byte e um int 4
bytes) [3 , 21, 4].
Os elementos ocupam posies contguas na memria. A alocao dos elementos da
matriz na memria pode ser feita colocando os elementos linha-por linha ou coluna-
por-coluna.
16
Prof: Jos Matias Pedro
i/j 0 1
0
0
1
0
Sendo C a quantidade de 0colunas por linhas, i o nmero da linha e j a posio do
elemento dentro linha, possvel definir a frmula genrica para acesso na memria,
onde Posij = endereo inicial + ((i-1) * C * tamanho do tipo do elemento) + ((j-1) * tamanho
do tipo do elemento).
Uma matriz consiste de dois ou mais vetores definidos por um conjunto de elementos. Cada
dimenso de uma matriz um vetor. O primeiro conjunto (dimenso) considerado o primeiro
vetor, o segundo conjunto o segundo vetor e assim sucessivamente.
A definio de uma matriz em C se d pela sintaxe:
tipo_do_dado nome_da_matriz[ quantidade_linhas ] [ quantidade_colunas ]
Matriz Letras
1 2 3 4 5 6 Colunas
1 1 1
1 M A R3 C O S
2 N A S S E R
1
3 D O N A L D
1
Linhas
17
Prof: Jos Matias Pedro
{
int iLinha, iColuna;
int iDeterminante;
int iValorA;
int aMatriz [DIMENSAO][DIMENSAO]
/* Uma regra que se pode sempre levar em considerao:
para cada dimenso de uma matriz, sempre haver um lao (normalmente um for). Se
houver duas dimenses, ento haver dois laos. */
for (iLinha=0; iLinha < DIMENSAO; iLinha++)
{
for (iColuna=0; iColuna < DIMENSAO; iColuna++)
{
printf ("Entre item %d %d:", iLinha + 1, iColuna + 1);
scanf ("%d", &iValorA);
matriz [iLinha][iColuna] = iValorA;
}
}
iDeterminante = aMatriz[0][0] * aMatriz [1][1] -
aMatriz[0][1] * aMatriz [1][0];
printf ("Determinante : %d\n", iDeterminante);
return 0;
}
Exemplo de uso de Matriz com Varias dimenses:
/* programa_matriz */
#include <stdio.h>
#define DIM_1
#define DIM_2
#define DIM_3
#define DIM_4
int main (void)
{
int i,j,k,l;
int aMatriz [DIM_1][DIM_2 ][DIM_3 ][DIM_4];
/* Cdigo para zerar uma matriz de quatro dimenses */
for (i=0; i < DIM_1; i++)
{
for (j=0; j < DIM_ ; j++)
{
for (k=0; k < DIM_ ; k++)
{
for (l=0; l < DIM_4; l++)
{
aMatriz [i][j][k][l] = i+j+k+l;
}
}
}
}
/* Uma regra que se pode sempre levar em considerao: para cada dimenso de uma
matriz, sempre haver um lao (normalmente um for). Se houver quatro dimenses ento
haver quatro laos */
For (i=0; i < DIM_1; i++)
{
for (j=0; j < DIM_2 ; j++)
{
for (k=0; k < DIM_3 ; k++)
18
Prof: Jos Matias Pedro
{
for (l=0; l < DIM_4; l++)
{
printf("\nValor para matriz em [%d] [%d] [%d] [%d] = %d", i,j,k,l, aMatriz[i][j][k][l]);
}
}
}
}
return 0;
}
Ponteiro
A linguagem C implementa o conceito de ponteiro. O ponteiro um tipo de dado como
int, char ou float. A diferena do ponteiro em relao aos outros tipos de dados que
uma varivel que seja ponteiro guardar um endereo de memria.
Por meio deste endereo pode-se acessar a informao, dizendo que a varivel
ponteiro aponta para uma posio de memria. O maior problema em relao ao
ponteiro entender quando se est trabalhando com o seu valor, ou seja, o endereo,
e quando se est trabalhando com a informao apontada por ele.
Operador & e *
O primeiro operador de ponteiro &. Ele um operador unrio que devolve o
endereo na memria de seu operando. Por exemplo: m = &count; pe o endereo
na memria da varivel count em m. Esse endereo a posio interna da varivel
na memria do computador e no tem nenhuma relao com o valor de count. O
operador & tem como significado o endereo de. O segundo operador *, que o
complemento de &. O * um operador unrio que devolve o valor da varivel
localizada no endereo que o segue. Por exemplo, se m contm o endereo da
varivel count: q = *m; coloca o valor de count em q. O operador * tem como
significado no endereo de.
A declarao de uma varivel ponteiro dada pela colocao de um asterisco (*) na
frente de uma varivel de qualquer tipo. Na linguagem C, possvel definir ponteiros
para os tipos bsicos ou estruturas. A definio de um ponteiro no reserva espao
de memria para o seu valor e sim para o seu contedo. Antes de utilizar um ponteiro,
o mesmo deve ser inicializado, ou seja, deve ser colocado um endereo de memria
vlido para ser acessado posteriormente.
Um ponteiro pode ser utilizado de duas maneiras distintas. Uma maneira trabalhar
com o endereo armazenado no ponteiro e outro modo trabalhar com a rea de
memria apontada pelo ponteiro. Quando se quiser trabalhar com o endereo
armazenado no ponteiro, utiliza-se o seu nome sem o asterisco na frente. Sendo
assim qualquer operao realizada ser feita no endereo do ponteiro. Como, na
maioria dos casos, se deseja trabalhar com a memria apontada pelo ponteiro,
alterando ou acessando este valor, deve-se colocar um asterisco antes do nome do
ponteiro. Sendo assim, qualquer operao realizada ser feita no endereo de
memria apontado pelo ponteiro. O programa seguinte demostra a utilizao de
ponteiros para acesso memria.
/* programa_matriz */
#include <stdio.h>
int main (void)
{
int *piValor; /* ponteiro para inteiro */
19
Prof: Jos Matias Pedro
/* programa_ponteiro*/
#include <stdio.h>
Void soma (int, int, int *);
int main (void)
{
int iValorA;
int iValorB;
int iResultado;
printf ("Entre com os valores:");
scanf ("%d %d", &iValorA, &iValorB);
printf("Endereco de iResultado = %d\n", &iResultado);
Soma (iValorA, iValorB, &iResultado) ;/* est sendo passado o endereo de memria
da varivel, qualquer alterao estar
sendo realizada na memria */
printf ("Soma : %d\n", iResultado);
return 0;
}
void soma (int piValorA, int piValorB, int * piResultado)
{
printf("Endereco de piResultado = %d\n", piResultado);
/* o valor est sendo colocado diretamente na memria */
*piResultado = piValorA + piValorB;
return;
}
Acesso Linear
Supondo que, para um determinado clculo, necessario aceder aos lementos de
um vetor, o acesso a esses elementos pode ser feito de forma sequencial desde o
primeiro at ao ultimo. Esse um acesso de tipo linear.
Um acesso linear tem um nmero de iteraes proporcional ao nmero de elementos
do vetor, ou seja o numero total de intrues executadas proporcional ao nmero
de elementos de entrada, sendo, por isso, uma funo de ordem linear, ou O(n).
20
Prof: Jos Matias Pedro
O cdigo seguinte calcula a soma dos elementos de um vetor de inteiros. Para obter
essa soma, tm que se consultar todos os elementos e acomula-los numa varivel. O
acesso linear e sequencial, o numero de iteraes executadas pela instruo for
igual ao nmero de elemento do vetor, ou seja, o nmero total de instrues
executadas proporcional ao nmero de elemento do vetor.
Soma dos elementos de um vetor
int soma(int v[ ], int tamanho){
int j, total=0;
for(j=0; j<tamanho; j++)
total +=v[ j ];
return total;
}
Na funo main do cdigo anterior, a funo soma invocada com os parmetros vtr
e 10, que so o nome do vetor e o seu tamanho.
Pesquisa Linear
No caso de se pretender pesquisar um vetor para averiguar se um determinado valor
existe nesse vetor, necessario percorrer o vetor, de forma sistemtica para garantir
a correo na pesquisa.
A forma mais universal de pesquisa de um vetor a pesquisa linear: percorre-se o
vetor do inicio at ao fim e avalia-se, posio a posio, se o valor ai contido o valor
procurado.
21
Prof: Jos Matias Pedro
No exemplo seguinte a funo procura recebe, por parmetro, o vetor, o seu tamanho
e o valor a pesquisar; e devolve o valor 1 no caso de encontrar o valor procurado, e
o valor 0 no caso contrario. Note-se que necessario percorrer o vetor at ao fim para
se poder afirmar que o valor no existe, ou seja, a instruo return 0 s invocado
aps o ciclo for terminar.
# include<stdio.h>
int procura (int v [ ], int tam, int valor){
int i;
for (i=0; i<tam; i++)
if (v [ i] ==valor) return i;
return -1;
}
int main() {
int vtr[10]={5,2,-3,7,10,15,-2,12,8,0};
int posicao, valor;
printf (Insira um numero a pesquisar:);
scanf(%d, &valor);
posicao=procura(vtr,10,valor);
if (posicao!=-1) printf(Existe na posicao:%d\n, posicao);
else printf(No existe\n);
}
A funo anterior de ordem linear, ou O(n). Em mdia, e caso o valor pesquisado
exista no vetor, o nmero de iteraes n/2.
Por exemplo, se o vetor tiver 1000 elementos, so necessarias 500 iteraes, em
mdia, para se encontrar um determinado elemento, caso exista.
3.Analise de Complexidade
Algoritmo: sequencia de instrues necessarias para a resoluo de um problema
bem formulado (passiveis de implementao em computador)
Estrategia:
especificar (definir propriedades)
arquitectura (algoritmo e estruturas de dados)
analise de complexidade (tempo de execuo e memoria)
implementar (numa linguagem de programao)
testar (submeter entradas e verificar observancia das propriedades
especificadas)
22
Prof: Jos Matias Pedro
prever o crescimento dos recursos exigidos por um algoritmo a medida
que o tamanho dos dados de entrada cresce.
Que dados usar ?
dados reais: verdadeira medida do custo de execuo
dados aleatorios: assegura-nos que as experincias testam o algoritmo
e no apenas os dados especificos.
Caso medio
dados perversos: mostram que o algoritmo funciona com qualquer tipo
de dados
Pior caso!
dados benficos:
Melhor caso
23
Prof: Jos Matias Pedro
24
Prof: Jos Matias Pedro
if (A[i]>max) 2 ops
max =A[i]; 2 ops n-1 vez
i=i+1; 2 ops
}
return max; 1 operao
}
25
Prof: Jos Matias Pedro
Normalmente, olharemos para o pior caso, pois d-nos um limite superior do tempo
de execuo.
Calculo do tempo de execuo
seja a o tempo observado para a operao primitiva mais rpida
seja b o tempo observado para a operao primitiva mais lenta
ento, o pior tempo de execuo T(n) ser
a(7n2) <=T(n) <=b(7n2) ) T(n) limitado por 2 funes lineares
O exemplo findMax()
mostrava T(n) limitado por 2 funes lineares em n, significando que o tempo
de execuo varia na mesma proporo que n.
Logo, diz-se que o crescimento de T(n) linear.
Tempo de processamento
Um algoritmo que realiza uma tarefa em 10 horas melhor que outro que
realiza em 10 dias.
26
Prof: Jos Matias Pedro
27
Prof: Jos Matias Pedro
3! = 3* 2 !
4! = 4* 3 !
Logo o fatorial de um nmero tambm pode ser definido recursivamente (ou por
recorrncia) atravs das seguintes regras (representao matemtica):
n! = 1, se n = 0
n! = n * (n-1)! , se n > 0
O programa seguinte mostra a verso recursiva do programa fatorial.
int fatorialr( int n)
{
int t, f;
/* condio de parada */
if( n == 1 || n == 0)
{
return 1;
}
f = fatorialr(n-1)*n; /* chamada da funo */
return f;
}
A verso no-recursiva de fatorial deve ser clara. Ela usa um lao que executado
de 1 a n e multiplica progressivamente cada nmero pelo produto mvel.
A operao de fatorial recursiva um pouco mais complexa. Quando fatorialr
chamada com um argumento de 1, a funo devolve 1. Caso contrrio, ela devolve o
produto de fatorialr(n-1)*n. Para avaliar essa expresso, fatorialr chamada com n-
1. Isso acontece at que n se iguale a 1 e as chamadas funo comecem a retornar.
Calculando o fatorial de 2, a primeira chamada a fatorialr provoca uma segunda
chamada com o argumento 1. Essa chamada retorna 1, que , ento, multiplicado por
2 (o valor original e n). A resposta ento 2.
Para melhor entendimento, interessante ver como o programa executado
internamente no computador. No caso do programa iterativo (programa seguinte)
necessrio duas variveis f e t para armazenar os diversos passos do processamento.
Por exemplo, ao calcular fatorial de 6, o computador vai passar sucessivamente pelos
seguintes passos (tabela ).
Tabela : Clculo de fatorial de 6
t f
1 1
2 2
3 6
4 24
5 120
6 720
28
Prof: Jos Matias Pedro
6 * 5 * fatorialr(4)
6 * 5 * 4 * fatorialr(3)
6 * 5 * 4 * 3 * fatorialr(2)
6 * 5 * 4 * 3 * 2 * fatorialr(1)
6*5*4*3*2*1
6*5*4*3*2
6*5*4*6
6 * 5 * 24
6 * 120
720
Nmeros de Fibonacci
Fibonacci (matemtico da Renascena italiana) estabeleceu uma srie curiosa de
nmeros para modelar o nmero de casais de coelhos em sucessivas geraes.
Assumindo que nas primeiras duas geraes s existe um casal de coelhos, a
seqncia de Fibonacci a seqncia de inteiros: 1, 1, 2, 3 , 5 , 8, 13 , 21,3 4, ....
No programa seguinte mostrada uma verso iterativa para calcular o n-simo termo
da seqncia de Fibonacci.
Programa: Clculo do n-simo termo de Fibonacci (verso iterativa)
int fibc (int n)
{
int l, h, x, i;
if ( n <=2 )
return 1;
l = 0;
h = 1;
for(i=2 ; i<= n; i++)
{
/* Clculo do prximo nmero da seqncia. */
x = l;
l = h;
h = x + l;
}
return h;
}
O n-simo nmero definido como sendo a soma dos dois nmeros anteriores.
Logo, fazendo a definio recursiva:
fib(n) = n se n <=
fib(n) = ib(n- ) + ib(n-1) se n >
A sua determinao recursiva impe o clculo direto do valor para dois elementos de
base (a primeira e a segunda gerao). No programa seguintes mostrada a verso
recursiva para calcular o n-simo termo da seqncia de Fibonacci.
Programa: Clculo do n-simo termo de Fibonacci (verso recursiva)
int fibr (int n )
29
Prof: Jos Matias Pedro
{
if ( n <= )
{
return 1;
}
/* chama a si prprio 2 vezes!!! */
return fibr (n-1) + fibr (n-2 );
}
Dados Heterogneos
Uma estrutura de dados chamada de heterognea quando envolve a utilizao de
mais de um tipo bsico de dado (inteiro ou caractere, por exemplo) para representar
uma estrutura de dados. Normalmente, este tipo de dado chamado de registro.
Um registro uma estrutura de dados que agrupa dados de tipos distintos ou, mais
raramente, do mesmo tipo. Um registro de dados composto por certo nmero de
campos de dados, que so itens de dados individuais. Registros so conjuntos de
dados logicamente relacionados, mas de tipos diferentes (numricos, lgicos,
caractere etc).
O conceito de registro visa facilitar o agrupamento de variveis que no so do
mesmo tipo, mas que guardam estreita relao lgica. Registros correspondem a
conjuntos de posies de memria conhecidos por um mesmo nome e
individualizados por identiicadores associados a cada conjunto de posies.
O registro um caso mais geral de varivel composta na qual os elementos do
conjunto no precisam ser, necessariamente, homogneos ou do mesmo tipo. O
registro constitudo por componentes. Cada tipo de dado armazenado em um
registro chamado de campo.
A linguagem C utiliza as estruturas para representar um registro. Com a estrutura
definida pode-se fazer atribuio de variveis do mesmo tipo de maneira simpliicada.
Programa: Exemplo de estrutura
struct Funcionario
{
char nome [40];
struct
{
30
Prof: Jos Matias Pedro
int dia;
int mes;
int ano;
} dataNasc;
char departamento[10];
float salario;
};
Para se fazer o acesso de um nico campo deve-se utilizar o nome da estrutura
seguido de um ponto e do nome do campo desejado da estrutura. A linguagem C
tambm permite que seja criado um vetor de estruturas (programa).
Programa : Exemplo de uso de estruturas com vetores
/* programa_estrutura */
#include <stdio.h>
struct DADO
{
char sNome[40];
int iIdade;
};
int main(void)
{
Struct DADO sDados [5];
/* A estrutura dividida em duas partes por um ponto (.). Tem-se o nome da estrutura
esquerda e o nome do campo direita. Neste exemplo, como est sendo manipulado um
vetor de estruturas, tambm tem ndice para cada linha do vetor. */
for(iIndice=0;iIndice<5 ;iIndice++)
{
printf("\nEntre com o Nome ->" );
scanf("%s", &sDados[iIndice].sNome );
printf("Entre com a Idade ->" );
scanf("%d", &sDados[iIndice].iIdade );
}
for(iIndice=0;iIndice<5 ;iIndice++)
{
printf("\n%s tem %d anos", sDados[iIndice].sNome, sDados[iIndice].iIdade);
}
return;
}
Lembrete: Estruturas so utilizadas para referenciar mltiplos tipos de dados.
31
Prof: Jos Matias Pedro
32
Prof: Jos Matias Pedro
Impresso da lista
para imprimir sequencialmente todos os elementos de uma lista, pode percorrer-se a
lista do inicio at ao fim e imprimir cada elemento. A soluo usual, nestes casos,
utilizar um apontador auxiliar que, de inicio, tem o endereo da cabea da lista, e
depois vai recebendo sucessivamente o endereo do elemento seguinte
Multiplas listas
A soluo anterior foi criada com o fito de ser simples, mas peca por falta de
generecidade. Com essa soluo, no caso de se pretender trabalhar com duas listas
de alunos em simultneo apontadas, por exemplo, pelas variveis turma1 e turma2,
necessario duplicar as funes de insero e impresso e substituir numas a
varivel cabea por turma1 e nas outras por turma2.
Para resolver este problema de falta de generecidade, deve passar-se a cabea da
lista, por parmetro, para as funes de insero e impresso.
A funo de impresso da lista apenas consulta a cabea no a modifica portanto
no levanta problemas adicionais.
Remoo
A remoo de um elemento de uma lista duplamente ligada uma operao
relactivamente simples de executar.
Note-se que necessrio tratar de forma diferente os casos extremos de remoo do
primeiro elemento e do ltimo elemento de uma lista.
5.2. Pilha
Uma das estruturas de dados mais simples a pilha. Possivelmente por essa razo,
a estrutura de dados mais utilizada em programao, sendo inclusive implementada
diretamente pelo hardware da maioria das mquinas modernas. A idia fundamental
da pilha que todo o acesso a seus elementos feito atravs do seu topo. Assim,
quando um elemento novo introduzido na pilha, passa a ser o elemento do topo, e
o nico elemento que pode ser removido da pilha o do topo. Isto faz com que os
elementos da pilha sejam retirados na ordem inversa ordem em que foram
introduzidos: o primeiro que sai o ltimo que entrou (a sigla LIFO last in, first out
usada para descrever esta estratgia).
Para entendermos o funcionamento de uma estrutura de pilha, podemos fazer uma
analogia com uma pilha de pratos. Se quisermos adicionar um prato na pilha, o
colocamos no topo. Para pegar um prato da pilha, retiramos o do topo. Assim, temos
que retirar o prato do topo para ter acesso ao prximo prato. A estrutura de pilha
funciona de maneira anloga. Cada novo elemento inserido no topo e s temos
acesso ao elemento do topo da pilha.
Existem duas operaes bsicas que devem ser implementadas numa estrutura de
pilha: a operao para empilhar um novo elemento, inserindo-o no topo, e a operao
para desempilhar um elemento, removendo-o do topo. comum nos referirmos a
essas duas operaes pelos termos em ingls push (empilhar) e pop (desempilhar).
33
Prof: Jos Matias Pedro
34
Prof: Jos Matias Pedro
Para inserir um elemento na pilha, usamos a prxima posio livre do vetor. Devemos
ainda assegurar que exista espao para a insero do novo elemento, tendo em vista
que trata-se de um vetor com dimenso fixa.
void push (Pilha* p, float v)
{
if (p->n == MAX) { /* capacidade esgotada */
printf("Capacidade da pilha estourou.\n");
exit(1); /* aborta programa */
}
/* insere elemento na prxima posio livre */
p->vet[p->n] = v;
p->n++;
}
A funo pop retira o elemento do topo da pilha, fornecendo seu valor como retorno.
Podemos tambm verificar se a pilha est ou no vazia.
float pop (Pilha* p)
{
float v;
if (vazia(p)) {
printf("Pilha vazia.\n");
exit(1); /* aborta programa */
}
/* retira elemento do topo */
v = p->vet[p->n-1];
p->n--;
return v;
}
A funo que verifica se a pilha est vazia pode ser dada por:
int vazia (Pilha* p)
{
return (p->n == 0);
}
Finalmente, a funo para liberar a memria alocada pela pilha pode ser:
void libera (Pilha* p)
{
free(p);
}
35
Prof: Jos Matias Pedro
struct pilha {
No* prim;
};
A funo cria aloca a estrutura da pilha e inicializa a lista como sendo vazia.
Pilha* cria (void)
{
Pilha* p = (Pilha*) malloc(sizeof(Pilha));
p->prim = NULL;
return p;
}
O primeiro elemento da lista representa o topo da pilha. Cada novo elemento
inserido no incio da lista e, conseqentemente, sempre que solicitado, retiramos o
elemento tambm do incio da lista. Desta forma, precisamos de duas funes
auxiliares da lista: para inserir no incio e para remover do incio. Ambas as funes
retornam o novo primeiro n da lista.
36
Prof: Jos Matias Pedro
}
Por fim, a funo que libera a pilha deve antes liberar todos os elementos da lista.
void libera (Pilha* p)
{
No* q = p->prim;
while (q!=NULL) {
No* t = q->prox;
free (q);
q = t;
}
free(p);
}
A rigor, pela definio da estrutura de pilha, s temos acesso ao elemento do topo.
No entanto, para testar o cdigo, pode ser til implementarmos uma funo que
imprima os valores armazenados na pilha. Os cdigos abaixo ilustram a
implementao dessa funo nas duas verses de pilha (vetor e lista). A ordem de
impresso adotada do topo para a base.
/* imprime: verso com vetor */
void imprime (Pilha* p)
{
int i;
for (i=p->n-1; i>=0; i--)
printf("%f\n",p->vet[i]);
}
/* imprime: verso com lista */
void imprime (Pilha* p)
{
No* q;
for (q=p->prim; q!=NULL; q=q->prox)
printf("%f\n",q->info);
}
5.3. Filas
Uma fila um conjunto ordenado de itens a partir do qual se podem eliminar itens
numa extremidade - incio da fila - e no qual se podem inserir itens na outra
extremidade - final da fila.
Ela uma prima prxima da pilha, pois os itens so inseridos e removidos de cordo
com o princpio de que o primeiro que entra o primeiro que sai -first in, first out (FIFO). O
conceito de fila existe no mundo real, vide exemplos como filas de banco, pedgios,
restaurantes etc. As operaes bsicas de uma fila so:
insert ou enqueue - insere itens numa fila (ao final).
remove ou dequeue - retira itens de uma fila (primeiro item).
empty - verifica se a fila est vazia.
size - retorna o tamanho da fila.
front - retorna o prximo item da fila sem retirar o mesmo da fila.
A operao insert ou enqueue sempre pode ser executada, uma vez que
teoricamente uma fila no tem limite. A operao remove ou dequeue s pode ser
aplicado se a fila no estiver vazia, causando um erro de underlow ou fila vazia se esta
operao for realizada nesta situao.
37
Prof: Jos Matias Pedro
Filas em C
A exemplo do que ocorre com estrutura em pilha, antes de programar a soluo de
um problema que usa uma fila, necessrio determinar como representar uma fila
usando as estruturas de dados existentes na linguagem de programao. Novamente
na linguagem C podemos usar um vetor. Mas a fila uma estrutura dinmica e pode
crescer infinitamente, enquanto que um vetor na linguagem C tem um tamanho fixo.
Contudo, pode-se definir este vetor com um tamanho suficientemente grande para
conter a fila.
5.4. rvores
Nos captulos anteriores examinamos as estruturas de dados que podem ser
chamadas de unidimensionais ou lineares, como vetores e listas. A importncia
dessas estruturas inegvel, mas elas no so adequadas para representarmos
dados que devem ser dispostos de maneira hierrquica. Por exemplo, os arquivos
(documentos) que criamos num computador so armazenados dentro de uma
estrutura hierrquica de diretrios (pastas). Existe um diretrio base dentro do qual
podemos armazenar diversos subdiretrios e arquivos. Por sua vez, dentro dos sub-
diretrios, podemos armazenar outros sub-diretrios e arquivos, e assim por diante,
recursivamente. A Figura seguinte mostra uma imagem de uma rvore de diretrio no
Windows 2000.
Neste captulo, vamos introduzir rvores, que so estruturas de dados adequadas para
a representao de hierarquias. A forma mais natural para definirmos uma estrutura
de rvore usando recursividade. Uma rvore composta por um conjunto de ns.
Existe um n r, denominado raiz, que contm zero ou mais sub-rvores, cujas razes
so ligadas diretamente a r. Esses ns razes das sub-rvores so ditos filhos do n
pai, r. Ns com filhos so comumente chamados de ns internos e ns que no tm
filhos so chamados de folhas, ou ns externos. tradicional desenhar as rvores
com a raiz para cima e folhas para baixo, ao contrrio do que seria de se esperar.
Sub-rvores
Estrutura de rvore
38
Prof: Jos Matias Pedro
rvores AVL
As rvores AVL so rvores binarias ordenadas e equilibradas. O nome AVL
composto pelas iniciais dos apelidos dos matemticos G.M.Adelson-Velskii e E. M.
Landis que propuseram este tipo de estruturas pela primeira vez, em 1962.
rvores binrias
Um exemplo de utilizao de rvores binrias est na avaliao de expresses. Como
trabalhamos com operadores que esperam um ou dois operandos, os ns da rvore
para representar uma expresso tm no mximo dois filhos. Nessa rvore, os ns
folhas representam operandos e os ns internos operadores. Uma rvore que
representa, por exemplo a expresso (3+6)*(4-1)+5 ilustrada na Figura seguinte.
*
5
+
+
-
3 6 4 1
+ +
rvore da expresso: (3+6) * (4-1) + 5.
Numa rvore binria, cada n tem zero, um ou dois filhos. De maneira recursiva,
podemos definir uma rvore binria como sendo:
uma rvore vazia; ou
um n raiz tendo duas sub-rvores, identificadas como a sub-rvore da direita
(sad) e a sub-rvore da esquerda (sae).
A Figura a seguir ilustra uma estrutura de rvore binria. Os ns a, b, c, d, e, f formam
uma rvore binria da seguinte maneira: a rvore composta do n a, da subrvore
esquerda formada por b e d, e da sub-rvore direita formada por c, e e f. O n a
representa a raiz da rvore e os ns b e c as razes das sub-rvores. Finalmente, os
ns d, e e f so folhas da rvore.
39
Prof: Jos Matias Pedro
a
A
F
b i c
F g F
i u i
g r g
d a e u f
u
F Exemplo1de rvore
F binria
r F
r
Representao em C ai 3 i a i
Anlogo ao que fizemos para g as demais
. gestruturas
1 degdados, podemos definir um tipo
1
para representar uma rvore u binria. Para
u simplificar ua discusso, vamos considerar
3 5 3
que a informao que queremos
r armazenar
r nos rns da rvore so valores de
caracteres simples. Vamos a
. inicialmente .
discutir como podemos representar uma
a s a 5 a
5
estrutura de rvore binria em C. Que estrutura podemos usar para representar um
1 e 1 a 1
n da rvore? a
Cada n deve armazenars3trs informaes:
g 3 a sinformao3 propriamente dita, no caso
.
um caractere, e dois ponteiros parau as . e
sub-rvores, . esquerda e direita. Ento a
e
5
estrutura de C para representar o n 5 5
i da rvoreg pode ser dada por:
g
struct arv { a r a u a
u
char info; s s s
i i
struct arv* esq; ie e e
struct arv* dir; r l r
g u g i g
}; i
u u u
Da mesma forma que uma s
l lista encadeada l representada por um ponteiro para o
primeiro n, a estrutura dauirvore como
t um i todo i
u representada por um ponteiro para
o n raiz. r r r s r
s
Como acontece com qualquer i TAD a(tipo iabstrato i
t de dados), as operaes que fazem
t
l
sentido para uma rvorerbinria dependem
u l l
essencialmente
r da forma de utilizao
que se pretende fazer da urvore. Nas m u
funes a que u
se seguem, consideraremos que
a
existe o tipo Arv definido por:
s s s
a u
typedef struct arv Arv; ut t t
Como veremos as funes e
m que manipulam m
rvores so,
r s r a r em geral, implementadas de
forma recursiva, usando aa definio recursiva da estrutura.
a a
t apenas a
e operaes
Vamos procurar identificar e e descrever cuja utilidade seja a mais
geral possvel. Uma operaou u s
que rprovavelmente u
dever ser includa em todos os
s
casos a inicializao detmuma rvoreu m
vazia. Como
t m
uma rvore representada pelo
endereo do n raiz, umaarvore vazia t atem que a
r ser representada pelo valor NULL.
r
e uma rvore
Assim, a funo que inicializa
u u e u podeeser simplesmente:
vazia
Arv* inicializa(void) s s s
t r t
{ t t t
return NULL; u a u
r d r r r
} r
u podemos e uter uma u
a operao
a
Para criar rvores no vazias, que cria um n raiz dadas
t
a informao e suas duasdsub-rvores, t
esquerda d t
e direita. Essa funo tem como
valor de retorno o endereo u do n raiz
r u
criado e pode user dada por:
e
Arv* cria(char c, Arv* sae, r Arv* sad){
v r r
Arv* p= (Arv*) malloca (sizeof
o
(Arv));
a r a
r
d r d v d
v
e e e o e
o 40
b r
r
r i r e r
e
Prof: Jos Matias Pedro
p->info = c;
p->esq = sae;
p->dir = sad;
return p;
}
As duas funes inicializa e cria representam os dois casos da definio recursiva
de rvore binria: uma rvore binria (Arv* a;) vazia (a = inicializa();) ou
composta por uma raiz e duas sub-rvores (a = cria(c,sae,sad);). Assim, com posse
dessas duas funes, podemos criar rvores mais complexas.
6. Algoritmos de ordenamento
Ordenao o processo de arranjar um conjunto de informaes semelhantes em
uma ordem crescente ou decrescente. Especificamente, dada uma lista ordenada i
de n elementos, ento: i1 <= i2 <= ... <= In. Algoritmo de ordenao em cincia da
computao um algoritmo que coloca os elementos de uma dada sequncia em
uma certa ordem - em outras palavras, efetua sua ordenao completa ou parcial. As
ordens mais usadas so a numrica e a lexicogrfica.
Existem vrias razes para se ordenar uma sequncia, uma delas a possibilidade
se acessar seus dados de modo mais eficiente.
41
Prof: Jos Matias Pedro
6.2. QuickSort
O algoritmo QuickSort do tipo diviso e conquista. Um algoritmo deste tipo resolve
vrios problemas quebrando um determinado problema em mais (e menores)
subproblemas.
O algoritmo, publicado pelo professor C.A.R. Hoare em 1962 , baseia-se na idia
simples de partir um vetor (ou lista a ser ordenada) em dois subvetores, de tal maneira
que todos os elementos do primeiro vetor sejam menores ou iguais a todos os
elementos do segundo vetor. Estabelecida a diviso, o problema estar resolvido,
pois aplicando recursivamente a mesma tcnica a cada um dos subvetores, o vetor
estar ordenado ao se obter um subvetor de apenas 1 elemento.
Os passos para ordenar uma sequncia S = {a1; a2 ; a3 ; ; an} dado por:
Seleciona um elemento do conjunto S. O elemento selecionado (p) chamado
de piv.
Retire p de S e particione os elementos restantes de S em seqncias distintas,
L e G.
A partio L dever ter os elementos menores ou iguais ao elemento piv p,
enquanto que a partio G conter os elementos maiores ou iguais a p.
Aplique novamente o algoritmo nas parties L e G.
Para organizar os itens, tais que os menores fiquem na primeira partio e os maiores
na segunda partio, basta percorrer o vetor do incio para o fim e do fim para o incio
simultaneamente, trocando os elementos. Ao encontrar-se no meio da lista, tem-se a
certeza de que os menores esto na primeira partio e os maiores na segunda
partio.
A figura seguinte, ilustra o que se passa quando se faz a partio de um vetor com a
sequncia de elementos S = { 7, 1, 3, 9, 8, 4, 2, 7, 4, 2, 3, 5 }. Neste caso o piv 4, pois o
valor do elemento que est na sexta posio (e 6 igual a 1 +12/2).
A escolha do elemento piv arbitrria, pegar o elemento mdio apenas uma das
possveis implementaes no algoritmo. Outro mtodo para a escolha do piv
consiste em escolher trs (ou mais) elementos randomicamente da lista, ordenar esta
sublista e pegar o elemento mdio.
Primeira troca: posies 1 e 11. Piv= 4.
7 1 3 9 8 4 2 7 4 2 3 5
Segunda troca: posies 4 e 10
3 1 3 9 8 4 2 7 4 2 7 5
Terceira troca: posies 5 e 9
3 1 3 2 8 4 2 7 4 9 7 5
Quarta troca: posies 6 e 7
3 1 3 2 4 4 2 7 8 9 7 5
3 1 3 2 4 2 4 7 8 9 7 5
Os precursos cruzaram-se; agora repete-se o procedimento recursivamente, para os
subvetores entre as posies 1 e 6 e entre as posies 7 e 12.
42
Prof: Jos Matias Pedro
6.3. MergeSort
Como o algoritmo QuickSort, o MergeSort outro exemplo de algoritmo do tipo diviso
e conquista, sendo um algoritmo de ordenao por intercalao ou segmentao. A
idia bsica a facilidade de criar uma seqncia ordenada a partir de duas outras
tambm ordenadas. Para isso, o algoritmo divide a seqncia original em pares de
dados, ordena-as; depois as agrupa em sequncias de quatro elementos, e assim por
diante, at ter toda a seqncia dividida em apenas duas partes.
Ento, os passos para o algoritmo so:
Dividir uma seqncia em duas novas seqncias.
Ordenar, recursivamente, cada uma das seqncias (dividindo novamente, quando
possvel).
Combinar (merge) as subseqncias para obter o resultado final.
Nas figuras seguintes podem ser vistos exemplos de ordenao utilizando os passos do
algoritmo.
3 1 4 1 5 9 2 6 5 4
3 1 4 1 5 9 2 6 5 4
3 1 4 1 5 9 2 6 5 4
3 1 4 1 5 9 2 6 5 4
1 3 1 5 2 9 6 5 4
4 5
1 5
1 4 5 4 5 6
1 1 3 4 5 2 4 5 6 9
1 1 2 3 4 4 5 5 6 9
43
Prof: Jos Matias Pedro
7. Tabela de Disperso
Neste captulo, vamos estudar as estruturas de dados conhecidas como tabelas de
disperso (hash tables), que, se bem projetadas, podem ser usadas para buscar um
elemento da tabela em ordem constante: O(1). O preo pago por essa eficincia ser
um uso maior de memria, mas, como veremos, esse uso excedente no precisa ser
to grande, e proporcional ao nmero de elementos armazenados.
Para apresentar a idia das tabelas de disperso, vamos considerar um exemplo
onde desejamos armazenar os dados referentes aos alunos de uma disciplina. Cada
aluno individualmente identificado pelo seu nmero de matrcula. Podemos ento
usar o nmero de matrcula como chave de busca do conjunto de alunos
armazenados. Na UMN, o nmero de matrcula dos alunos dado por uma seqncia
de oito dgitos, sendo que o ltimo representa um dgito de controle, no sendo
portanto parte efetiva do nmero de matrcula. Por exemplo, na matricula 9711234-4,
o ultimo dgito 4, aps o hfen, representa o dgito de controle. O nmero de matrcula
efetivo nesse caso composto pelos primeiros sete dgitos: 9711234.
Para permitir um acesso a qualquer aluno em ordem constante, podemos usar o
nmero de matrcula do aluno como ndice de um vetor vet. Se isso for possvel,
acessamos os dados do aluno cuja matrcula dado por mat indexando o vetor
vet[mat]. Dessa forma, o acesso ao elemento se d em ordem constante, imediata. O
problema que encontramos que, nesse caso, o preo pago para se ter esse acesso
rpido muito grande.
Vamos considerar que a informao associada a cada aluno seja representada pela
estrutura abaixo:
struct aluno {
int mat;
char nome[81];
char email[41];
char turma;
};
typedef struct aluno Aluno;
Como a matrcula composta por sete dgitos, o nmero inteiro que conceitualmente
representa uma matrcula varia de 0000000 a 9999999. Portanto, precisamos
dimensionar nosso vetor com dez milhes (10.000.000) de elementos. Isso pode ser
feito por:
#define MAX 10000000
Aluno vet[MAX];
Dessa forma, o nome do aluno com matrcula mat acessado simplesmente por:
vet[mat].nome. Temos um acesso rpido, mas pagamos um preo em uso de
memria proibitivo. Como a estrutura de cada aluno, no nosso exemplo, ocupa pelo
menos 127 bytes, estamos falando num gasto de 1.270.000.000 bytes, ou seja, acima
de 1 Gbyte de memria. Como na prtica teremos, digamos, em torno de 50 alunos
cadastrados, precisaramos apenas de algo em torno de 6.350 (=127*50) bytes.
Para amenizar o problema, j vimos que podemos ter um vetor de ponteiros,em vez
de um vetor de estruturas. Desta forma, as posies do vetor que no correspondem
a alunos cadastrados teriam valores NULL. Para cada aluno cadastrado, alocaramos
44
Prof: Jos Matias Pedro
Indicadores sequenciais
Periodo de ingresso
Ano de ingresso
Numa turma de aluno, comum existirem vrios alunos com o mesmo ano e perodo
de ingresso. Portanto, esses trs primeiros dgitos no so bons candidatos para
identificar individualmente cada aluno. Reduzimos nosso problema para uma chave
com os quatro dgitos seqenciais. Podemos ir alm e constatar que os nmeros
seqenciais mais significativos so os ltimos, pois num universo de uma turma de
alunos, o dgito que representa a unidade varia mais do que o dgito que representa
o milhar.
Desta forma, podemos usar um nmero de matrcula parcial, de acordo com a
dimenso que queremos que tenha nossa tabela (ou nosso vetor). Por exemplo, para
dimensionarmos nossa tabela com apenas 100 elementos, podemos usar apenas os
ltimos dois dgitos seqenciais do nmero de matrcula. A tabela pode ento ser
declarada por:
Aluno* tab[100].
Para acessarmos o nome do aluno cujo nmero de matrcula dado por mat, usamos
como ndice da tabela apenas os dois ltimos dgitos. Isso pode ser conseguido
aplicando-se o operador modulo (%): vet[mat%100]->nome.
Desta forma, o uso de memria excedente pequeno e o acesso a um determinado
aluno, a partir do nmero de matrcula, continua imediato. O problema que surge
que provavelmente existiro dois ou mais alunos da turma que apresentaro os
mesmos ltimos dois dgitos no nmero de matrcula. Dizemos que h uma coliso,
pois alunos diferentes so mapeados para o mesmo ndice da tabela. Para que a
estrutura funcione de maneira adequada, temos que resolver esse problema, tratando
as colises.
Existem diferentes mtodos para tratarmos as colises em tabelas de disperso, e
estudaremos esses mtodos mais adiante. No momento, vale salientar que no h
como eliminar completamente a ocorrncia de colises em tabelas de disperso.
Devemos minimizar as colises e usar um mtodo que, mesmo com colises,
saibamos identificar cada elemento da tabela individualmente.
Funo de disperso
A funo de disperso (funo de hash) mapea uma chave de busca num ndice da
tabela. Por exemplo, no caso exemplificado acima, adotamos como funo de hash a
utilizao dos dois ltimos dgitos do nmero de matrcula. A implementao dessa
45
Prof: Jos Matias Pedro
46
Prof: Jos Matias Pedro
47
Prof: Jos Matias Pedro
Referncias Bibliograficas
Apostila_estrutura_dados, profs. Waldemar Celes e Jos Lucas Rangel
PUC-RIO-Curso de engenharia-2002.
Algoritmos e Estrutura de Dados I, Marcos Castilho, Fabiano Silva Daniel. Agosto de
2015.
Projecto e Analise de algoritmos: Introduo, prof. Humberto Brando.
Introduo a complexidade de algoritmos :Fernando Silva, DCC-FCUP. Estrutura de
Dados.
Linguagem C: Algoritmos de Ordenao, prof. Paulo R.S.L. Coelho.
Tabela de Disperso, W. Celes e J.L.Rangel.
Algoritmo e C: Alexandre Pereira.
WEISS, Mark Allen. Data Structures and Problem Solving Using java, 3nd Edition.
Pearson, Addison Wesley, 2006.
DROZDEK, Adam. Data Structures and Algorithms in C++. Thomson Learning lnc,
2005.
48