Estruturas de Dados e Algoritmos

João Arthur Brunet
Computação @ UFCG

Particionamento Hoare para o Quick Sort


No material sobre o QuickSort nós vimos o particionamento de Lomuto – um algoritmo simples para posicionar o pivot tal que todos os elementos à esquerda são menores ou iguais e todos os elementos à direita são maiores. Dá uma olhada lá para revisar o que é particionamento, porque nesse material nós vamos direto ao ponto: mostrar como funciona a estratégia de Hoare, que é mais eficiente na prática do que o de Lomuto.

A estratégia de Hoare pra particionar

Vamos particionar o array $[3, 8, 7, 10, 0, 23, 2, 1, 77, 7]$.

Vamos aqui também trabalhar com o pivot na primeira posição, isto é, pivot = v[ini]. A ideia do particionamento de Hoare é usar dois índices para percorrer o array. O primeiro índice, i, parte da posição à frente do pivot, isto é, ini + 1 e percorre o array do início para o fim. O segundo índice, j, parte da última posição e percorre o array do fim para o início. Sempre que encontrar um elemento v[i] maior do que o pivot, o i pára. De forma análoga, sempre que encontrar um elemento v[j] menor do que o pivot, o j pára. Depois dessa primeira iteração, se i < j, troca-se v[i] por v[j] e repete-se esse processo. Complicado? Assim escrito é um pouco, né? Vamos para o exemplo:

Para o array $values = [3, 1, 2, 3, 10, 23, 2, 1, 77, 7]$, temos que $pivot = 3$, $i = 1$ e $j = 9$, porque pivot é o valor de v[ini], i sempre inicia com ini + 1 e j sempre inicia no índice fim.

Passo 1. Vamos iterar no array com i primeiro, identificando o primeiro valor maior do que o pivot. Enquanto for menor, apenas anda com i. No nosso caso então, i vai parar no índice 4, já que 10 é maior que o pivot 3.

Passo 2. Vamos iterar com j agora, identificando o primeiro valor menor do que o pivot. Enquanto for maior, apenas anda com j para trás. No nosso caso então, j vai parar no índice 7, já que 1 é menor que o pivot 3.

values = [3, 1, 2, 3, 10, 23, 2, 1, 77, 7]

Agora perguntamos: i < j? Sim, pois 4 é menor do que 7. Então trocamos v[i] por v[j], ou seja, trocamos 10 por 1. Note que estamos mantendo os menores valores imediatamente a frente do pivot e os maiores na parte final do array, por isso trocamos.

values = [3, 1, 2, 3, 1, 23, 2, 10, 77, 7]

Acabou? Não, pois i ainda é menor ou igual a j, isto é, não ultrapassou j. Nesse caso, continuamos iterando com os dois. Vamos lá. i pararia no índice 5, pois 23 é maior do que 3. Já o j pararia no índice 6, já que 2 é menor do que 3.

values = [3, 1, 2, 3, 1, 23, 2, 77, 7]

Agora perguntamos: i < j? Sim, pois 5 é menor do que 6. Então trocamos v[i] por v[j], ou seja, trocamos 23 por 2.

values = [3, 1, 2, 3, 1, 2, 23, 77, 7]

Acabou? Não, pois i ainda é menor ou igual a j, isto é, não ultrapassou j. Nesse caso, continuamos iterando com os dois. Vamos lá. i pararia no índice 6, pois encontrou o valor 23, que é maior que o pivot. Já o j pararia no índice 5, já que 2 é menor do que 3.

Agora perguntamos: i < j? Não, pois 6 é maior do que 5. Nesse caso paramos a computação e apenas trocamos o pivot pelo elemento na posição j:

values = [2, 1, 2, 3, 1, 3, 23, 77, 7]

Particionou? Sim, né? Todos os valores à esquerda do pivot são menores ou iguais a ele e todos os valores à direita do pivot são maiores que ele.

Agora é só fazer como sempre, chamar o particionamento de forma recursiva para a esquerda e para a direita.


Acho que agora é um bom momento para você responder o quiz abaixo e verificar se você entendeu de fato a rotina de particionamento que acabamos de ver.


E o código?

De certa forma, simples. Veja:


public int particiona(int[] v, int ini, int fim) {

	int i = ini + 1;
	int j = fim;
	int pivot = v[ini];

	while (i <= j) {

		// andando com i para frente. pára quando encontrar um valor maior que o pivot
		while (i <= j && v[i] <= pivot)
			i++;

		// andando com j para trás. pára quando encontrar um valor menor ou igual ao pivot
		while(i <= j && A[j] > pivot)
                j = j - 1;
         
        // se i não encontrou j, troca
        if (i < j)
        	swap(v, i, j);

	}

	swap(v, ini, j);
	return j;
}