apostila
259 pág.

apostila

Disciplina:Algoritmos e Estrutura de Dados I542 materiais7.956 seguidores
Pré-visualização50 páginas
foi exigido no enunciado que fossem localizadas
todas as ocorreˆncias (este e´ outro problema). Enta˜o podemos modificar o co´digo para
a versa˜o apresentada na figura 10.16, simplesmente trocando-se o for por um while
que termina a execuc¸a˜o ta˜o logo o elemento seja encontrado (se for encontrado).

Este algoritmo e´ mais ra´pido, na me´dia, do que o anterior, embora o nu´mero de
comparac¸o˜es feitas possa ser a mesma no caso do elemento na˜o estar presente no
vetor. Mas, se dermos sorte, o elemento pode estar no in´ıcio do vetor e terminar bem
ra´pido. Na me´dia, espera-se que ele pare mais ou menos na metade do vetor, isto e´,
considerando-se uma distribuic¸a˜o uniforme dos elementos.

Mas como o lac¸o faz dois testes, no caso do elemento na˜o ser encontrado ele sera´
um pouco mais lento. Notem que o duplo teste no lac¸o e´ necessa´rio pois deve parar
ou porque achou o elemento ou porque o vetor terminou. Este segundo teste so´ vai
dar true uma u´nica vez, o que e´ um desped´ıcio.

Se pudesse haver garantia de que sempre o elemento procurado estivesse presente,
enta˜o poder´ıamos construir um teste simples, o que pode nos economizar alguma
computac¸a˜o. Esta garantia na˜o pode existir, certo? Mais ou menos. Digamos que o
programador deliberadamente coloque o elemento no vetor. Neste caso, ha´ a garantia

140 CAPI´TULO 10. ESTRUTURAS DE DADOS

function busca (x: real ; var v: vetor r ; n: integer) : integer ;
var i : integer ;

achou: boolean;

begin
achou:= false ;
i := 1;
while ( i <= n) and not achou do
begin

if v[ i ] = x then
achou:= true ;

i := i + 1;
end;
i f achou then

busca:= i − 1;
end;

Figura 10.16: Busca em vetores, segunda versa˜o.

de que ele esta´ presente. Mas algue´m pode perguntar: assim na˜o vale, se eu coloco
na˜o significa que ele ja´ estava presente, eu vou sempre encontrar o elemento “falso”.

Novamente, depende de onde o programador coloque o elemento. Digamos que ele
o coloque logo apo´s a u´ltima posic¸a˜o. Enta˜o, das duas uma: ou o elemento na˜o estava
no vetor original, e neste caso a busca pode terminar pois o elemento sera´ encontrado
apo´s a u´ltima posic¸a˜o; ou o elemento estava presente e sera´ encontrado antes daquele
que foi adicionado. Um teste final resolve a du´vida. Se for encontrado em posic¸a˜o
va´lida, e´ porque estava presente, sena˜o, na˜o estava.

Este elemento adicionado logo apo´s o final do vetor e´ denominado sentinela e seu
uso e´ ilustrado na versa˜o apresentada na figura 10.17.

function busca (x: real ; var v: vetor r ; n: integer) : integer ;
var i : integer ;

begin
v[n+1]:= x;
i:= 1;
while v[ i ] <> x do

i := i + 1;
i f i <= n then

busca:= i
else

busca:= 0;
end;

Figura 10.17: Busca em vetores com sentinela.

Apesar da melhoria, este algoritmo sempre faz um nu´mero de comparac¸o˜es que
pode atingir n no pior caso, isto e´, quando o elemento na˜o esta´ presente no vetor.

O caso o´timo e o caso me´dio na˜o mudam, embora o algoritmo, conforme explicado,

10.1. VETORES 141

fac¸a metade das comparac¸o˜es quando comparado com a versa˜o anterior. Desta forma
ele e´ apenas ligeiramente melhor do que o anterior.

Ainda, o programador deve garantir que a posic¸a˜o usada pelo sentinela nunca seja
usada como sendo um elemento va´lido, pois na˜o e´. O programador colocou o elemento
ali de maneira controlada, mas na˜o se trata de um elemento va´lido. Isto significa que
o nu´mero de elementos u´teis do vetor agora na˜o e´ mais max r (ou max i), mas sempre
um a menos. Normalmente se modifica a definic¸a˜o do tipo para prever este espac¸o
adicional para o programador.

const min r=0; max r=50;
min i=1; max i=10;

type vetor r= array [min r . .max r+1] of real ;
vetor i= array [ min i . . max i+1] of integer ;

E´ poss´ıvel tornar o algoritmo mais eficiente?
A resposta e´ sim2. Mas, para tornar a busca mais eficiente e´ preciso impor algumas

restric¸o˜es extra na forma como os dados sa˜o organizados em memo´ria..
Esta maneira consiste em considerar que os dados esta˜o de alguma forma respei-

tando uma ordem lexicogra´fica em memo´ria. Por exemplo, se forem nomes, esta˜o
em ordem alfabe´tica. Se forem nu´meros, esta˜o em ordem crescente (ou descrescente).
Porque isto e´ necessa´rio? Pois pode-se explorar a informac¸a˜o de ordenac¸a˜o para tornar
o me´todo mais eficiente.

No caso, podemos modificar a soluc¸a˜o anterior para que o algoritmo termine a
busca sempre que encontrar um elemento que ja´ e´ maior do que aquele que se esta´
procurando, pois o vetor esta´ ordenado. O programa da figura 10.18 foi implementado
com esta ideia.

function busca (x: real ; var v: vetor r ; n: integer) : integer ;
var i : integer ;

begin
v[n+1]:= x;
i:= 1;
while v[ i ] < x do

i := i + 1;
i f (v[ i ] = x) and ( i <= n) then

busca:= i
else

busca:= 0;
end;

Figura 10.18: Busca em vetores ordenados.

Apesar de termos explorado uma propriedade adicional que faz com que no caso

2Em disciplinas avanc¸adas de algoritmos se estudam me´todos bastante eficientes para este pro-
blema. No nosso caso veremos apenas um deles.

142 CAPI´TULO 10. ESTRUTURAS DE DADOS

me´dio a busca seja mais eficiente, no pior caso, aquele em que o elemento procurado
na˜o pertence ao vetor, ainda temos um algoritmo que faz tantas comparac¸o˜es quanto
o tamanho do vetor. E´ poss´ıvel fazer melhor? A resposta novamente e´ sim.

Como exemplo, considere o problema de se encontrar um verbete no diciona´rio.
Sabemos que estes verbetes se encontram em ordem alfabe´tica. Por este motivo,
ningue´m em sa˜ conscieˆncia procura um nome em ordem sequencial desde o in´ıcio, a
menos que esteja procurando algo que comec¸a com a letra “A”.

Suponha que o verbete inicia com a letra “J”. Normalmente se abre o diciona´rio
mais ou menos no meio. Se o nome presente no in´ıco da pa´gina for “menor” lexico-
graficamente falando do que aquele que se busca, por exemplo, algo comec¸ando com
a letra “D”, enta˜o, pode-se tranquilamente rasgar o diciona´rio, eliminando-se tudo o
que esta´ antes do “D” e continuar o processo com o que restou.

Na segunda tentativa abre-se novamente mais ou menos na metade do que sobrou.
Suponha que ca´ımos na letra “M”. Como estamos procurando o “J”, pode-se sem
problemas rasgar novamente o diciona´rio eliminando-se tudo o que segue o “M”, ate´
o fim. Resta procurar “apenas” entre o “D” e o “M”.

Aplicando-se consistentemente este processo repetidas vezes, sempre teremos um
diciona´rio dividido mais ou menos por 2. Se ele tinha 500 pa´ginas no in´ıcio, na segunda
vez tera´ 250 e na terceira 125, na quarta algo pro´ximo de 70 pa´ginas. E assim por
diante. Por isto que costumamos localizar rapidamente verbetes em um diciona´rio.

O algoritmo mostrado na figura 10.20, denominado de busca bina´ria, implementa
esta ideia. Procura-se o elemento no meio do vetor. Se encontrou, enta˜o pode parar.
Se na˜o encontrou, basta verificar se o valor encontrado e´ maior ou menor do que o
procurado. Se for maior, joga-se a metade superior fora e trabalha-se apenas com
a metade inferior. Se for menor, joga-se fora a metade inferior e trabalha-se apenas
com a metade superior. Novamente procura-se na metade do que sobrou e assim por
diante, ate´ que se encontre o elemento ou ate´ que se determine que na˜o e´ mais poss´ıvel
encontra´-lo.

Vamos agora comparar estas u´ltimas quatro verso˜es para o algoritmo de busca.
A tabela 10.19 resume o nu´mero de comparac¸o˜es feitas para diversos tamanhos de
entrada, sempre analisando o pior caso. O caso me´dio exige um pouco mais de cui-
dado para se calcular e na˜o sera´ estudado aqui. O caso o´timo e´ sempre uma u´nica
comparac¸a˜o para os casos: o algoritmo acha o elemento na primeira tentativa.

Versa˜o n = 10 n = 102 n = 104 n = 108

Busca simples (fig. 10.16) 20 200 20000 200000000
Busca com sentinela (fig. 10.17) 10 100 10000 100000000
Busca em vetor ordenado (fig.