Professional Documents
Culture Documents
Vers ao 2000.1
Estas notas de aula n ao devem ser usadas como u nica fonte de estudo. O aluno deve ler outros livros dispon veis na literatura. Nenhuma parte destas notas pode ser reproduzida, qualquer que seja a forma ou o meio, sem a permiss ao dos autores. Os autores concedem a permiss ao expl cita para a utilizac a o e reproduc a o deste material no contexto do ensino de disciplinas regulares dos cursos de graduac a o sob a responsabilidade do Instituto de Computac a o da UNICAMP.
Copyright 2000
Instituto de Computac a o UNICAMP Caixa Postal 6176 13083970 CampinasSP fkm,tomasz @ic.unicamp.br
ii
o 11 Algoritmos de Ordenac a
Nesta sec a o vamos considerar outros algoritmos para ordenac a o: InsertionSort, MergeSort e QuickSort. Apresentaremos os algoritmos MergeSort e QuickSort implementados recursivamente e utilizando uma importante t ecnica chamada Divis ao e Conquista. Estes dois algoritmos est ao entre os algoritmos mais r apidos para ordenac a o usando apenas comparac a o entre elementos. Neste tipo de ordenac a o, o algoritmo QuickSort e o que tem o melhor tempo m edio para se ordenar seq ue ncias em geral.
11.1
Algoritmo InsertionSort
O algoritmo InsertionSort tamb em usa a t ecnica de projeto de algoritmo por induc a o para resolver o problema, i.e., supondo saber resolver um problema pequeno, resolve se um maior.
Considere um vetor . Vamos supor que j a sabemos ordenar o vetor (com elementos, e portanto menor que ). A id eia e ordenar o vetor e depois inserir o elemento na posic a o correta (da o nome InsertionSort). Primeiramente apresentamos um algoritmo recursivo que implementa esta id eia. procedure InsertionSortRecursivo(var v : TipoVetorReal;n:integer); var i : integer; aux : real; begin if n 1 then begin InsertionSortRecursivo(v,n 1); Ordena os n 1 primeiros elementos aux: v[n]; i: n; while (i 1) and (v[i 1] aux) do begin v[i] : v[i 1]; i: i 1; end; v[i] : aux; end; end; InsertionSortRecursivo
13254
"!#%$ 1
&
"&'()"!#0$
2 6
O algoritmo tem uma estrat egia indutiva e ca bem simples implement a-lo de maneira recursiva. Note que a base da recurs ao e para o vetor com no m aximo 1 elemento (nestes caso o vetor j a est a ordenado e portanto o problema para este vetor j a est a resolvido). Uma vez que desenvolvemos a estrat egia, podemos observar que podemos descrev e-lo melhor de forma interativa, uma vez que o algoritmo acima faz uma chamada recursiva no in cio da rotina (veja sec a o 10.4). Assim, uma estrat egia mais eciente, usando duas estruturas de repetic a o, e apresentada a seguir. procedure InsertionSort(var v : TipoVetorReal;n:integer); var i,j : integer; aux : real; begin for i: 2 to n do begin aux: v[i]; j: i; while (j 1) and (v[j 1] aux) do begin v[j] : v[j 1]; j: j 1; end; v[j] : aux; end; end; InsertionSort
2 6
No caso m edio este algoritmo tamb em gasta um tempo computacional quadr atico em relac a o a quantidade de elementos. Por outro lado, se a inst ancia estiver quase ordenada, este algoritmo e bastante r apido.
107
11.2
O algoritmo MergeSort tamb em usa a estrat egia por induc a o. Al em disso, o algoritmo usa de outra t ecnica bastante importante chamada Divis ao e Conquista. Neste tipo de projeto, temos as seguintes etapas:
7 7
Problema sucientemente pequeno: Resolvido de forma direta. Problema n ao e sucientemente pequeno: Divis ao: O problema e dividido em problemas menores. Conquista: Cada problema menor e resolvido (recursivamente). Combinar: A soluc a o dos problemas menores e combinada de forma a construir a soluc a o do problema.
No algoritmo MergeSort, a etapa de divis ao consiste em dividir o vetor, com elementos, em dois vetores (sube dividido vetores) de mesmo tamanho ou diferindo de no m aximo um elemento. No caso, um vetor em vetores e .
389$
Para facilitar, em vez de realmente dividir o vetor em dois outros vetores, usaremos ndices para indicar o in cio e o m de um subvetor no vetor original. Assim, estaremos sempre trabalhando com o mesmo vetor, mas o subvetor a ser ordenado em cada chamada recursiva ser a denido atrav es destes ndices. Caso o vetor tenha no m aximo 1 elemento, o vetor j a est a ordenado e n ao precisamos subdividir em partes menores (base da recurs ao). Uma vez que o vetor (problema) foi dividido em dois subvetores (problemas menores), vamos ordenar (conquistar) cada subvetor (subproblema) recursivamente. o es dos subproblemas e feita intercalando os dois subvetores ordenados em apenas um A combinac a o das soluc vetor. Para isso, usaremos dois ndices para percorrer os dois subvetores e um terceiro para percorrer o vetor que receber a os dois vetores intercalados, vetor resultante. A id eia e comec ar os ndices no in cio dos dois vetores e comparar os dois elementos localizados por estes ndices e atribuir no vetor resultante o menor valor. Em seguida, incrementamos o ndice que tinha o menor valor. Este processo se repete at e que tenhamos intercalado os dois vetores no vetor resultante.
4`deXa
No quadro seguinte apresentamos o procedimento para intercalar os vetores e na gura 36 ilustramos seu comportamento.
IQPSRT9RVURVW9@X3Y(R`Wba e Q I PSX3Y(R`W3c
procedure IntercalaMergeSort(var v : TipoVetorReal; Inicio,Meio,Fim:integer); var i,j,k : integer; v1 [Inicio..Meio], v2 [Meio 1..Fim] begin supondo a declarac a o de Vaux no procedimento principal i: Inicio; j: Meio 1; k: Inicio; while (i Meio) and (j Fim) do begin if (v[i] v[j]) then begin Sempre inserindo o menor em Vaux vaux[k]: v[j]; j: j 1; end else begin vaux[k]: v[i]; i: i 1; end; k: k 1; end; while (i Meio) do begin vaux[k]: v[i]; Inserindo os elementos do primeiro vetor i: i 1; k: k 1; end; while (j Fim) do begin vaux[k]: v[j]; Inserindo os elementos do segundo vetor j: j 1; k: k 1; end; for k: Inicio to Fim do v[k] : vaux[k]; Copiando para o vetor original end; IntercalaMergeSort
fg 6
fh c c
fg fg
c c
c c
108
inicio
m+1
fim
V Vaux
1
i k
0
j
6
Configurao Inicial
1
i
2
j
Vaux 0
k
3
i
2
j
Vaux 0
1
k
3 1 3 1 1 1
7
i
8 3 8 3 3 3
9 4 9 4 4 4
0 5 0
i
2 6
6
j
Vaux 0 V 1
2 7 2 2 2
2 6 6 6
4 7 7 7
5 8 8 8
6
j Transferncia do segundo vetor.
Vaux 0 V 0
5 5
i
9
k
9 9
Vaux 0
Figura 36: Intercalac a o de dois sub-vetores. procedure MergeSort(var V : TipoVetorReal; n:integer); var Vaux : TipoVetorReal; vetor auxiliar para fazer intercalacoes
2i2i6
fi2i2p
procedure MergeSortRecursivo(var V : TipoVetorReal; inicio,m:integer); var meio : integer; begin if (m inicio) then begin Se tiver quantidade suciente de elementos meio: (m inicio) div 2; Dividindo o problema MergeSortRecursivo(V,inicio,meio); Conquistando subproblema 1 MergeSortRecursivo(V,meio 1,m); Conquistando subproblema 2 IntercalaMergeSort(V,inicio,meio,m); Combinando subproblemas 1 e 2 end; end; MergeSortRecursivo
Na gura 37 apresentamos uma simulac a o do algoritmo MergeSort para o vetor 7,3,8,1,4,2,6,5 . Denotamos a chamado da rotina MergeSort(V) por M(V), e a rotina IntercalaMergeSort por I( ). Se uma chamada da rotina exige chamadas recursivas, esta e substitu da pelas chamadas , onde e s ao as duas partes de .
q rIs$
I & I &&
109
M( 7 I( M( 7 I( I( M( 7 I( I( I(M(7) I( I( I( 7 I( I( I( 7 I( I( (3 I( I( (3 I( I( (3 I( I( (3 I( I( (3 I( (1 I( (
, 3 , 3 , 3) ,M(3)) ,M(3) ) , 3) , 7) , 7) , 7) , 7) , 7) , 3 3 2
, 1 , 4 , , 1) ,M(4 , , 1)) ,M(4 , , 1)) ,M(4 , , 1)) ,M(4 , , 1) ) ,M(4 , , 1) ) ,M(4 , , M(1))) ,M(4 , , M(1))) ,M(4 , , 1)) ,M(4 , , 8)) ,M(4 , , 8) ,M(4 , , , 8) , (2 4 , 5 , ,
2 , 6 2 , 6 2 , 6 2 , 6 2 , 6 2 , 6 2 , 6 2 , 6 2 , 6 2 , 6 2 , 6 2 , 6 4 6 , , 5 7
, 5) , 5) ) , 5) ) , 5) ) , 5) ) , 5) ) , 5) ) , 5) ) , 5) ) , 5) ) , 5) ) , 5) ) , , 6) ) 8)
(1 , 1 ,
Figura 37: Simulac a o das chamadas recursivas do procedimento MergeSort para o vetor 7,3,8,1,4,2,6,5 .
11.3
Algoritmo QuickSort
O algoritmo QuickSort tamb em usa a t ecnica de divis ao e conquista, dividindo cada problema inicial em duas partes. Neste caso, a etapa de divis ao em dois subproblemas e mais sosticada mas por outro lado a etapa de combinac a o e simples (lembrando que no algoritmo MergeSort, dividir e simples enquanto combinar e mais sosticado). Para dividir um vetor de um pivo tal que
@%$ em dois subvetores x&'x& xy & $ e "& &'"& & x& & $ , o algoritmo usa & , w4V e f & & , 4@ . ao ordenados recursivamente (conquistados). Uma vez que o vetor foi dividido nos subvetores & e & & , estes s Por m, os vetores s ao combinados, que nada mais e que a concatenac a o dos vetores ordenados x& e "& & .
"%$ . Primeiro escolhemos um pivo . Uma das vantagens de se escolher este pivo do pr oprio vetor a ser ordenado e que podemos implementar a estrat egia garantindo sempre que cada subvetor e estritamente menor que o vetor original. O que faremos e tomar o pivo e dividir o vetor em subvetores x& e "& & de tal maneira que a ordenac a o nal se torne o vetor &ed $ d & & , onde o operador d e a concatenac a o de vetores.
Antes de apresentar o algoritmo de particionamento em um vetor, vamos apresent a-lo usando um vetor auxiliar. A implementac a o e mais simples e ca mais f acil de se entender a id eia do programa. Primeiro a rotina escolhe um pivo ). Troca com o primeiro elemento ( ). Em seguida percorre-se todos (posic a o os demais elementos e insere os elementos menores ou iguais ao pivo, lado a lado, no in cio do vetor auxiliar. Os elementos maiores que o pivo s ao inseridos lado a lado a partir do m do vetor auxiliar. Por m, o pivo e inserido na posic a o intermedi aria e o vetor auxiliar e copiado para o vetor auxiliar.
A etapa mais complicada neste algoritmo e a etapa de divis ao, que pode ser feita ecientemente no mesmo vetor
IsPSRpT9R`UR`Wa
110
function ParticionaQuickSort(var v : TipoVetorReal;inicio,m:integer):integer; var esq,dir,IndicePivo:integer; Pivo:real; Supondo a declarac a a o de Vaux (vetor auxiliar p/ partic o) no procedimento principal begin troca(v[inicio],v[(inicio m) div 2]); escolha de um pivo Pivo : v[Inicio]; Pivo ca na posic a o Inicio esq: Inicio; dir: Fim; for i: Inicio 1 to m do begin if (V[i] Pivo) then begin Vaux[dir] : V[i]; dec(dir); end else begin inc(esq); Vaux[esq] : V[i]; end; end; V[esq] : Pivo; esq dir for i: Inicio to m do V[i] : Vaux[i]; Copia Vaux[inicio..m] para V[inicio..m] ParticionaQuickSort : esq; end; No quadro seguinte mostramos comportamento deste algoritmo.
inicio
Pivo
fim
V Vaux
8 12 7 13 9 10 2 15 9 17 7
Configurao Inicial esq Pivo i dir
V 10 12 7 13 9 Vaux
esq Pivo
2 15 9 17 7
dir i
V 10 12 7 13 9 Vaux
esq Pivo
2 15 9 17 7 12
dir
V 10 12 7 13 9 Vaux 7
esq Pivo i
2 15 9 17 7 12
dir
V 10 12 7 13 9 Vaux 7
esq Pivo
2 15 9 17 7 13 12
dir
V 10 12 7 13 9 Vaux 7
Pivo
8 7
2 15 9 17 7 17 15 13 12
esq=dir i
V 10 12 7 13 9 Vaux 7 9 8 2 9
2 15 9 17 7
7 10 17 15 13 12
esq=dir Pivo
Copia pivo na posio intermediria. i Copia vetor auxiliar para o vetor original.
9 9
8 8
2 2
9 9
7 10 17 15 13 12 7 10 17 15 13 12
esq=dir
Vaux 7
111
Agora vamos descrever o procedimento para particionar um vetor, atrav es de um pivo e sem uso de vetor auxiliar. Primeiramente colocaremos o pivo na primeira posic a o do vetor. Para obter os subvetores e , usaremos de dois ndices no vetor original, esq e dir. Inicialmente esq comec a no segundo elemento do vetor (logo depois do pivo) e dir comec a no u ltimo elemento do vetor. Em seguida, fazemos o ndice esq andar para a direita enquanto o ndice apontar para um elemento menor ou igual ao pivo. Fazemos o mesmo com o ndice dir, andando-o para a esquerda enquanto o elemento apontado por ele for maior que o pivo. Isto far a com que o ndice esq v a pulando todos os elendice dir pula todos os elementos que v ao pertencer ao vetor . Quando os mentos que v ao pertencer ao vetor e o dois ndices pararem, estes v ao estar parados em elementos que n ao s ao os pertencentes aos correspondentes vetores e neste caso, trocamos os dois elementos e continuamos o processo. Para manter a separac a o entre e vi avel, vamos fazer este processo sempre enquanto . Quando todo este processo terminar (i.e., quando ), iremos inserir o Pivo (que estava na primeira posic a o) separando os dois vetores. O algoritmo QuickSort est a descrito na p agina 112. Existem outras implementac o es do algoritmo QuickSort que s ao mais ecientes que a vers ao que apresentamos, embora s ao tamb em um pouco mais complicadas. function ParticionaQuickSort(var v : TipoVetorReal;inicio,m:integer):integer; var esq,dir,IndicePivo : integer; Pivo : real; begin troca(v[inicio],v[(inicio m) div 2]); escolha de um pivo Pivo : v[Inicio]; Pivo ca na posic a o Inicio esq: Inicio 1; primeiro elemento mais a esquerda, pulando o pivo dir: Fim; primeiro elemento mais a direita while (esq dir) do begin Pivo) do inc(esq); while (esq dir) and (v[esq] v[dir]) do dec(dir); while (esq dir) and (pivo if esq dir then begin troca(V[esq],V[dir]); inc(esq); dec(dir); end; esq, e esq dir 1 end; dir if v[dir] Pivo then IndicePivo : dir dir esq 1 ou dir esq else IndicePivo : dir 1; esq dir troca(v[IndicePivo],v[Inicio]); Coloca o pivo separando os dois subvetores ParticionaQuickSort : IndicePivo; end;
& &&
&
Y0|}fhRp~
c f f
fh
fh
fh
2 2
Sfh
A seguir, apresentamos a descric a o do algoritmo QuickSort. procedure QuickSort(var V : TipoVetorReal; n:integer); Declarar Vaux e inserir rotina Intercala ParticionaQuickSort procedure QuickSortRecursivo(var V :TipoVetorReal; inicio,m:integer); var IndicePivo : integer; begin if (m inicio) then begin se tem pelo menos 2 elementos IndicePivo : ParticionaQuickSort(v,Inicio,Fim); Dividindo o problema QuickSortRecursivo(V,Inicio,IndicePivo 1); Conquistando subproblema 1 QuickSortRecursivo(V,IndicePivo 1,Fim); Conquistando subproblema 2 end; N ao e preciso fazer nada para combinar os dois subproblemas end; begin QuickSortRecursivo(V,1,n); end;
2i2i6
fi2i2p
112
11.4
Exerc cios
1. Fac a uma rotina recursiva para ordenar um vetor usando a estrat egia do algoritmo SelectionSort. 2. Fac a um algoritmo para ordenar um vetor usando uma estrat egia parecida com a usada pelo algoritmo MergeSort s o que em vez de dividir em duas partes, divide em tr es partes. (I.e., se o vetor for sucientemente pequeno, ordena-se de forma direta. Caso contr ario este algoritmo divide o vetor em 3 partes iguais (ou diferindo de no m aximo um elemento). Ordena recursivamente cada um dos tr es subvetores e por m o algoritmo intercala os tr es vetores, obtendo o vetor ordenado.) elementos consecutivos de um vetor do ndice ao ndice . Projete um procedimento recur3. Seja sivo com o cabec alho procedure minmax(var v: TipoVetorReal; i,f: integer; var minimo, maximo: real); que retorna em minimo e maximo o menor e o maior valor que aparecem no vetor dos ndices ao ndice , respectivamente. O procedimento deve dividir o vetor em dois subvetores de tamanhos quase iguais (diferindo de no m aximo 1 elemento). 4. (K- esimo) O problema do - esimo consiste em, dado um vetor com elementos, encontrar o - esimo menor elemento do vetor. Note que se o vetor j a estiver ordenado, podemos resolver este problema obtendo o elemento a um algoritmo que usa a ordenac a o para resolver o problema do que est a na posic a o do vetor. Isto nos d - esimo. Entretanto, e poss vel se resolver este problema de maneira mais eciente, no caso m edio. A id eia e combinar duas estrat egias:
@bx$
(a) Estrat egia da busca bin aria, de descartar uma parte do vetor. (b) Estrat egia do algoritmo ParticionaQuickSort, que divide o vetor em tr es partes, onde cont em os elementos menores ou iguais a e cont em os elementos maiores que
I &
I &&
Use estas duas estrat egias para desenvolver uma func a o que dado , e , devolve o - esimo menor elemento do vetor. 5. Fac a um programa contendo procedimentos com os seguintes cabec alhos:
I 1
7 7 7 7 7
procedure MergeSort(var v:TipoVetor; :integer); procedure QuickSort(var v:TipoVetor; :integer); procedure SelectionSort(var v:TipoVetor; :integer); procedure InsertionSort(var v:TipoVetor; :integer); procedure aleatorio(var v:TipoVetor; :integer);
onde TipoVetor e um vetor de n umeros: type TipoVetor=array [1..30000] of integer; As rotinas MergeSort, QuickSort, InsertionSort e SelectionSort s ao rotinas para ordenar usando os algoritmos do mesmo nome. A rotina aleatorio coloca no vetor a quantidade de elementos gerados (pseudo) aleatoriamente pela func a o random(MAXINT).
O programa deve ter rotinas auxiliares para leitura e impress ao de vetores. O programa deve ter um menu com opcoes: (a) (b) (c) (d) (e) (f) (g) (h) Ler um vetor com inteiros ( tamb em e lido). Inicialmente Gerar vetor aleat orio com elementos ( e lido). Ordenar por MergeSort. Ordenar por QuickSort. Ordenar por InsertionSort. Ordenar por SelectionSort. Imprimir o vetor. Sair do programa.
1 .
O aluno deve cronometrar os tempos gastos pelas rotinas de ordenac a o para ordenar inteiros (pseudo) aleat orios, onde pode ser (ou o quanto o computador suportar). Caso algum m etodo seja muito lento, ignore sua execuc a o. N ao e necess ario entregar estes tempos.
14))%))%4)))4))b%0ub)b%ubb)9b)b
113
1p
e"$ 4 1 4
1Q
casas do tabuleiro. (a) Diga se o cavalo consegue fazer um passeio passando por todas as (b) Dado uma posic a o , e , diga se existe um passeio da casa de origem at ea . Em caso positivo, imprima uma seq ue ncia de movimentos que o cavalo precisa fazer para ir de casa para .
e"$
Vi$
Vi$ 4 1 4
1
e$
Vi$
7. (Problema das 8 Rainhas) O problema das 8 rainhas consiste em encontrar uma disposic a o de 8 rainhas em um tabuleiro de xadrez. Uma rainha pode atacar qualquer outra que esteja na mesma linha, coluna ou diagonal do tabuleiro. Portanto o objetivo e dispor as 8 rainhas de maneira que qualquer duas delas n ao se ataquem. Fac a um programa que encontra a soluc a o para este problema, com rainhas em um tabuleiro . 8. (Pentominos) Um Pentomino e uma gura formada juntando 5 quadrados iguais pelos lados. As 12 guras poss veis, exceto por rotac o es, s ao as seguintes:
11
Dado um ret angulo , o objetivo e preencher este ret angulo com os pentominos, sem sobreposic a o entre pentominos e sem que um pentomino ultrapasse a borda do ret angulo. Por exemplo, a gura seguinte mostra um preenchida com pentominos. Al em disso, um pentomino pode ser girado antes de ser encaixado ret angulo no ret angulo.
k34
Fac a um programa recursivo para resolver este problemas, dado um ret angulo
114