Estrutura de dados | | Classificação rápida, classificação rápida de duas vias, classificação rápida de três vias

Classificação rápida, classificação rápida de duas vias, classificação rápida de três vias


1. Classificação rápida

1. Conceito

A classificação rápida usa a ideia de dividir e conquistar para classificar os dados

  1. Escolha um benchmark

  2. Coloque o valor menor que o valor de referência à esquerda do valor de referência e o resto (maior ou igual a) à direita

  3. Em seguida, continue a dividir os lados esquerdo e direito até que a duração do intervalo dividido seja 1.

2. Complexidade de tempo

A classificação rápida é O (logN) ao dividir o intervalo, e cada vez que leva O (N) para classificar

Portanto, a complexidade de tempo da classificação rápida é O (NlogN) , que é o caso quando o intervalo pode ser dividido em partes iguais para o valor de referência

Se os dados já estão ordenados, cada vez que você divide e conquista, não há dados de um lado, mas dados do outro lado. Isso é o mesmo que classificação por bolha, e a complexidade de tempo é O (N ^ 2)

Em suma:

A melhor complexidade de tempo: O (NlogN)

Pior complexidade de tempo: O (N ^ 2)

3. Cenários adequados

A classificação rápida é adequada para situações em que os dados estão fora de ordem. Quando os dados estão em ordem, a complexidade do tempo é provavelmente O (N ^ 2), o que não reflete as vantagens da classificação rápida.

4. Otimização

  1. Quando dividido em pequenas áreas, a classificação por inserção é usada em vez da classificação rápida

  2. Para a implementação recursiva da linha rápida pode ser alterado para não recursivo, usando a pilha para alcançar

  3. Cada vez que você seleciona o valor de referência, você não seleciona mais diretamente o primeiro ou o último elemento.

Você pode usar o método do meio de três números ou escolher um valor de referência aleatoriamente

5. Realização

Existem três métodos de implementação para a função de partição de classificação rápida:

  1. Método do ponteiro esquerdo e direito

  2. Método de escavação

  3. Método de ponteiro frontal e traseiro

O método de ponteiros para frente e para trás é usado aqui para alcançar

//前后指针法
int Partion(int array[], int left, int right)
{
    int cur = left;
    int prev = cur - 1;
    while (cur < right)
    {
        if (array[cur] <= array[right] && ++prev != cur)
        {
            std::swap(array[cur], array[prev]);
        }
        cur++;
    }
    std::swap(array[++prev], array[right]);
​
    return prev;
}
​
void QuickSort(int array[], int left, int right)
{
    if (array == nullptr)
    {
        return;
    }
​
    if (left < right)
    {
        int index = Partion(array, left, right);
        QuickSort(array, left, index - 1);
        QuickSort(array, index + 1, right);
    }
}

Outros métodos de implementação e código otimizado: https://github.com/YKitty/LinuxDir/blob/master/DS/Sort/Sort.c

2. Fila rápida bidirecional

1. O motivo

Para classificação rápida, se houver muitos elementos de dados e o tamanho dos elementos estiver muito próximo, quando a esquerda e a direita dividem e conquistam, isso fará com que todos os dados de um lado e nenhum dado do outro lado , o que levará a uma eficiência reduzida e complexidade de tempo Torna-se O (N ^ 2)

Exemplo: Use um modelo para testar o tempo de classificação por mesclagem e classificação rápida, defina uma matriz de 1000000, os elementos da matriz são selecionados aleatoriamente entre 0-10, então leva 0,290727s para usar a fusão e 171,151s para classificação rápida, sim, você não tem Errado. Quando a classificação rápida é ótima, é o (nlgn) e, nesse momento, obviamente degenera ao nível de o (n ^ 2). Por que é que?

Para a classificação rápida que escrevi acima, todos os dados menores ou iguais ao valor do benchmark são colocados à esquerda, e os dados maiores do que o benchmark são colocados à direita, então haverá problemas. Não importa quando a condição é maior ou igual ou menor ou igual a, quando há muitos elementos repetidos na matriz, há muitos elementos iguais ao valor de referência, a matriz será dividida em duas partes que são extremamente desequilibradas, porque a parte igual ao valor de referência está sempre concentrada Ao lado da matriz.

Neste momento, o uso da linha rápida bidirecional pode ser otimizado para evitar a redução da eficiência.

Problemas resolvidos pela linha rápida bidirecional:

Não deixe todos os elementos iguais ao valor de referência se concentrarem em um lado da matriz.

2. Pensamento

  1. Colocamos todos os elementos menores que o valor de referência no lado esquerdo da matriz e os elementos maiores que o valor de referência no lado direito da matriz

  2. Para a esquerda com um índice à esquerda , à direita há um índice à direita

  3. Quando a esquerda é menor que o valor de referência , ele continua retrocedendo ++ até encontrar uma esquerda maior ou igual ao valor de referência ; quando a direita é maior que o valor de referência , continua avançando - até encontrar um direito inferior ou igual ao valor de referência

  4. Neste momento , os elementos de dados à esquerda e à direita são trocados e, em seguida, à esquerda ++, à direita -

  5. Continue a executar o No. 2 até que a esquerda == a direita pare

  6. Neste caso, os elementos de valor de referência de troca e posição esquerda , retornados ao valor de referência, podem ser

Esse tipo de pensamento, mesmo que haja muitos elementos de dados repetidos, eles podem ser separados quase igualmente, e não causará uma grande quantidade de dados de um lado e nenhum dado do outro.

3. Cenários adequados

Quando há muitos elementos repetidos na matriz , você não pode usar a classificação rápida, apenas use a classificação rápida bidirecional

4. Realização

Ao implementar, só precisa mudar, a função de partição

int Partion_Two(int array[], int left, int right)
{
    int l = left;
    int r = right - 1;
    while (1)
    {
        while (l <= r && array[l] < array[right])
        {
            l++;
        }
        while (l <= r && array[r] > array[right])
        {
            r--;
        }
        if (l > r)
        {
            break;
        }
        std::swap(array[r], array[l]);
    }
    std::swap(array[l], array[right]);
​
    return r;
}

Nota: Encontre o que não é menor do que à esquerda e o que não é maior à direita e, em seguida, execute o julgamento e a troca

3. Fila rápida de três vias

1. Pensamento

Divida a matriz em três partes, menor que o valor de referência, maior que o valor de referência e igual ao valor de referência

Registre os três subscritos:

lt: O último subscrito menor que o valor de referência

gt: o primeiro subscrito maior que o valor de referência

índice: o subscrito sendo percorrido

índice é menor que o valor de referência: índice de câmbio e lt + 1, lt ++

índice é maior que o valor de referência: índice de troca e gt-1, gt--

índice é igual ao valor de referência: índice ++

Condição final: índice == gt

A última etapa é trocar os valores de referência:

troca (arr [lt], arr [direita])

Intervalo contínuo:

[esquerda, lt-1]

[gt, certo]

 

2. Realização

void QuickSort_Three(int array[], int left, int right)
{
	if (array == nullptr)
	{
		return;
	}

	if (right <= left)
	{
		return;
	}

	int lt = left - 1;
	int gt = right;
	int index = left;
	int key = array[right];
	while (index < gt)
	{
		if (array[index] < key)
		{
			std::swap(array[index], array[lt + 1]);
			lt++;
			index++;
		}
		else if (array[index] > key)
		{
			std::swap(array[index], array[gt - 1]);
			gt--;
		}
		else
		{
			index++;
		}
	}
	std::swap(array[index], array[right]);
	QuickSort_Three(array, left, lt );
	QuickSort_Three(array, gt, right);
}

 

Acho que você gosta

Origin blog.csdn.net/qq_40399012/article/details/89059038
Recomendado
Clasificación