Você está na página 1de 10

Fundamentos da Programação

aula 09: Ainda mais manipulações de listas

1 Introdução

< figura: a menina que controla a menina >

Nós vamos começar a aula de hoje com a seguinte pergunta

• Quantos bonequinhos são necessários para ordenar uma lista ?

Bom, a resposta é: três

2 3


9 4 10 2 6 17 8 12 7 15

Quer dizer,

- um deles encontra o maior elemento da lista


- o outro troca o maior elemento de lugar com o último
- e o terceiro controla a repetição, para ordenar a lista completamente

1
E a ideia é que

- primeiro o maior elemento é colocado na última posição


- daı́ o segundo maior elemento é colocado na penúltima posição
- daı́ o terceiro maior elemento é colocado na ante-penúltima posição
- e assim por diante ...

A coisa fica assim

#include <stdio.h>
#include <stdlib.h>

#define N 10

int L[N] = { 9, 4, 10, 2, 6, 17, 8, 12, 7, 15 };

void Troca ( int L[], int i, int j )


{
int aux;

aux = L[i];
L[i] = L[j]; // troca
L[j] = aux;
}

int Encontra_Maior_na_faixa ( int L[], int inicio, int fim )


{
int i, M, posM;

M = L[inicio]; posM = inicio;

for ( i=inicio+1; i<=fim; i++ )


{
if ( L[i] > M ) { M = L[i]; posM = i; }
}
return ( posM );
}

int main ()
{
int k; // controle da repetiç~
ao
int p;

for ( k=N-1 ; k>0 ; k-- )


{
p = Encontra_Maior_na_faixa ( L, 0, k );

Troca ( L, p, k );
}
}

2
2 Ainda mais manipulação de listas

Legal.

Mas, essa não é a única maneira de fazer as coisas.

Quer dizer, a gente também pode ordenar a lista por meio de varreduras.

E fazendo as coisas desse modo, a gente também usa 3 bonequinhos

1
(repetidor)

2 (varredor)

3 (trocador)

9 4 10 2 6 17 8 12 7 15

A coisa fica assim

#include <stdio.h>
#include <stdlib.h>

#define N 10

int L[N] = { 9, 4, 10, 2, 6, 17, 8, 12, 7, 15 };

void Troca ( int L[], int i, int j )


{
int aux;

aux = L[i]; L[i] = L[j]; L[j] = aux;


}

int Varredura ( int L[], int inicio, int fim )


{
int i;

for ( i=inicio+1 ; i<=fim ; i++ )


{
if ( L[i] > L[i+1] ) Troca ( L, i, i+1 );
}
}

int main ()
{
int k; // controle da repetiç~
ao

for ( k=N-1 ; k>0 ; k-- )

Varredura ( L, 0, k );
}

3
Mais exemplos

a. Outro método de ordenação

Certo.
Mas a gente também pode fazer as coisas ao contrário.
Quer dizer, a ideia é fazer varreduras para a esquerda.
Onde as varreduras vão trazendo os elementos pequenos para o inı́cio da lista.
E as faixas da varredura vão ficando cada vez maiores

9 4 10 2 6 17 8 12 7 15

...

Para implementar essa ideia, a gente vai utilizar 3 bonequinhos mais uma vez

1
(repetidor)

9 4 10 2 6 17 8 12 7 15

3 (trocador)

2 (varredor)

A coisa fica assim

( ... )

void Troca ( int L[], int i, int j )


{
int aux;

aux = L[i]; L[i] = L[j]; L[j] = aux;


}

int Varredura_para_esquerda ( int L[], int inicio, int fim )


{
int i;

for ( i=fim ; i<inicio ; i++ )


{
if ( L[i] < L[i-1] ) Troca ( L, i, i-1 );
} }

4
int main ()
{
int k; // controle da repetiç~
ao

for ( k=1 ; k<N ; k++ )

Varredura_para_esquerda ( L, 0, k );
}

b. Metades ordenadas

Agora imagine que nós temos uma lista onde a primeira e a segunda metades já estão ordenadas

Por exemplo,
L 2 6 7 10 13 5 8 9 12 15

A tarefa consiste em

→ Colocar a lista completamente em ordem

Mas, como é que a gente faz isso?


Bom, a gente pode usar uma lista auxiliar.
E daı́, a gente usa 3 dedos.
Quer dizer, a gente coloca

- um dedo no inı́cio da primeira metada da lista L


- um dedo no inı́cio da segunda metada da lista L
- e um dedo no inı́cio da lista auxiliar A

Os dois primeiros dedos vão sendo deslocados para o fim das suas metades.
E o menor elemento apontado por esses dedos é copiado para a lista auxiliar.

• Quantos bonequinhos a gente precisa para fazer isso ?

Bom, 2 bonequinhos bastam

i j
↓ ↓
L 2 6 7 10 13 5 8 9 12 15

? 2

A

k

5
Quer dizer,

- um bonequinho controla a movimentação dos dedos


- o o outro copia os elementos para a lista auxiliar

A coisa fica assim

#include <stdio.h>
#include <stdlib.h>

#define N 10

int L[N] = { 9, 4, 10, 2, 6, 17, 8, 12, 7, 15 };

int Copia_menor ( int L[], int A[], int i, int j, int k )


{
if ( i == N/2 ) // 1a metade já acabou
{
A[k] = L[j];
retorna (2); // elemento da 2a metade foi copiado
}

else if ( j == N ) // 2a metade já acabou


{
A[k] = L[i];
retorna (1); // elemento da 1a metade foi copiado
}

else if ( L[i] < L[j] ) // compara menores de cada metade


{
A[k] = L[i];
retorna (1); // elemento da 1a metade foi copiado
}
else
{
A[k] = L[j];
retorna (2); // elemento da 2a metade foi copiado
}
}

int main()
{
int i = 0, j = N/2, k = 0; // 3 dedos
int m;
int A[N];

while ( i < N/2 || j < N ) // enquanto existe metade n~


ao-vazia
{
m = Copia_menor ( L, A, i, j, k );

if ( m == 1 ) i++;
else j++;
k++;
}

// Etapa final: copia elementos de volta p/ a lista L


for ( i=0 ; i<N ; i++ )
L[i] = A[i];
} 

6
c. Mais um método de ordenação

Certo.
Agora está na hora de encaixotar tudo o que a gente fez na aula de hoje.
Mas para fazer isso, é preciso dar nomes aos bois.

O método de ordenação que nós vimos na Introdução é chamado de ordenação por seleção
— (porque?)
E agora, nós podemos colocá-lo dentro da sua caixinha

void Ord_Selecao ( int L[], int tam )


{
int k, p;

for ( k=tam-1 ; k>0 ; k-- )


{
p = Encontra_pos_Maior ( L, 0, k );
Troca ( L, p, k );
}
}

Daı́, o método que nós vimos no inı́cio dessa seção é chamado de ordenação da bolha
— (porque?)
E agora, nós podemos colocá-lo dentro da sua caixinha

void Ord_Bolha ( int L[], int tam )


{
int k;

for ( k=tam-1 ; k>0 ; k-- )


{
Varredura ( L, 0, k );
}
}

Daı́, o método que nós vimos no inı́cio dessa seção é chamado de ordenação por inserção
— (porque?)
E agora, nós podemos colocá-lo dentro da sua caixinha

void Ord_Inserç~
ao ( int L[], int tam )
{
int k;

for ( k=1 ; k<tam ; k++ )


{
Varredura_esq ( L, 0, k );
}
}

7
Daı́, o procedimento que nós vimos no exemplo (b) é chamado de intercalação
— (porque?)
E agora, nós podemos colocá-lo dentro da sua caixinha

void Intercalaç~
ao ( int L[], int tam )
{
int i = 0, j = tam/2, k = 0; // 3 dedos
int m;
int A[tam];

while ( i < tam/2 || j < tam ) // enquanto existe metade n~


ao-vazia
{
m = Copia_menor ( L, A, i, j, k );

if ( m == 1 ) i++;
else j++;
k++;
}

for ( i=0 ; i<tam ; i++ ) L[i] = A[i];


}

Legal.
Muito bom.
Agora tudo está encaixotado.
E agora nós podemos fazer algumas observações interessantes.
A primeira delas é que os 3 métodos de ordenação são bem parecidos, mas eles não são a mesma
coisa.
Quer dizer, se a gente ordenar uma lista muito, muito grande com os 3 métodos, a gente vai ver
que

- a ordenação por seleção é mais rápida que a ordenação da bolha


- a ordenação por inserção é mais rápida que a ordenação por seleção

Veja só

ord bolha: .....


ord seleç~
ao: .....
ord inserç~
ao: .....

— (porque?)
Mas, isso ainda não é o mais legal.
Quer dizer, agora que tudo está encaixotado, a gente pode pensar em ainda mais um método de
ordenação.
Quer dizer, a ideia é muito simples.
Nós já sabemos fazer a intercalação, não é?

=⇒

8
Então, basta usar uma das 3 caixinhas para ordenar as duas metades, e depois aplicar a intercalação

=⇒

=⇒

A coisa fica assim

#include <stdio.h>
#include <stdlib.h>

#define N 10

int L[N] = { 9, 4, 10, 2, 6, 17, 8, 12, 7, 15 };

( ... )

int main()
{
Ordena ( L, 0, N/2 - 1); // a sua caixinha favorita de ordenaç~
ao

Ordena ( L, N/2, N - 1); // a sua caixinha favorita de ordenaç~


ao

Intercalacao ( L, N );
}

Legal, não é?



d. Ordenação dentro da ordenação

Sim, é legal.
O nome desse métod é ordenação via intercalação.
E agora, nós também podemos colocá-lo dentro de uma caixinha

int Ord_intercalacao()
{
Ordena ( L, 0, N/2 - 1); // a sua caixinha favorita de ordenaç~
ao

Ordena ( L, N/2, N - 1); // a sua caixinha favorita de ordenaç~


ao

Intercalacao ( L, N );
}

Mas, você quer ouvir uma boa notı́cia?

Bom, a boa notı́cia é que a ordenação por intercalação é ainda mais rápida do que a ordenação
por inserção — (quando a lista é muito, muito grande ...)

ord inserç~
ao: .....
ord intercalaç~
ao: .....

— (porque?)

9
Ora, mas quando a gente fica sabendo disso, a gente acaba tendo outra ideia.
Quer dizer, olhe outra vez para o esquema da ordenação por intercalação

=⇒

=⇒

Na primeira etapa a gente ordena as duas metades usando uma das nossas 3 caixinhas de
ordenação.
Mas, peraı́!
A ordenação por intercalação é mais rápida do que qualquer uma das 3 caixinhas.
Então, faz sentido ordenar as metades usando a ordenação via intercalação

Ordena ( L, 0, N/2 - 1); −→ Ord Intercalacao ( L, 0, N/2 - 1);

Ordena ( L, N/2, N - 1); −→ Ord Intercalacao ( L, N/2, N - 1 );

Intercalacao ( L, N );

Isso nos dá um novo método de ordenação, que a gente pode colocar dentro de uma caixinha.
Mas para isso, é preciso ter um nome.
E para dar o nome, é preciso entender como ele funciona.
Quer dizer, o que está acontecendo é o seguinte

=⇒ (ordenação)

=⇒ (intercalação)

=⇒ (intercalação)

Logo, a gente pode chamar esse método de ordenação via intercalação dupla.

E agora, nós podemos colocá-lo dentro de uma caixinha

int Ord_intercalacao_dupla()
{
Ordena ( L, 0, N/2 - 1); // a sua caixinha favorita de ordenaç~
ao

Ordena ( L, N/2, N - 1); // a sua caixinha favorita de ordenaç~


ao

Intercalacao ( L, N );
}

10

Você também pode gostar