Estrutura de dados: algoritmo de classificação comum (5): classificação de heap (implementação C ++)

Estrutura de dados: algoritmo de classificação comum (5): classificação de heap

A ordenação de heap é um tipo de ordenação por seleção de árvore, que é uma melhoria efetiva da ordenação por seleção direta. Heap é uma estrutura de dados de árvore especial, ou seja, uma árvore binária completa. O heap é dividido em um grande heap de raiz e um pequeno heap de raiz. O grande heap de raiz significa que o valor do nó raiz é maior que o valor dos dois nós filhos; o pequeno heap de raiz significa que o valor do nó raiz é menor que o valor dos dois nós filhos e as duas subárvores do nó raiz também são uma pilha.

A definição de uma pilha: uma sequência com n elementos (h1, h2, ..., hn), se e somente se (hi> = h2i, hi> = 2i + 1) ou (hi <= h2i, hi <= 2i +1) (i = 1,2, ..., n / 2) é chamado de heap. Somente o heap que atende às condições anteriores é discutido aqui. Pode-se ver na definição do heap que o elemento superior (ou seja, o primeiro elemento) deve ser o maior item (heap superior grande). Uma árvore binária completa pode representar intuitivamente a estrutura do heap. O topo da pilha é a raiz e as outras são as subárvores esquerda e direita. (Pode ser estendido para a passagem de pré-pedido, passagem de ordem média e passagem de pós-pedido)

1. Ideia básica :

No início, a sequência de números a serem ordenados é considerada uma árvore binária armazenada sequencialmente, e sua ordem de armazenamento é ajustada para torná-la um heap.Neste momento, o número de nós raiz do heap é o maior. Em seguida, troque o nó raiz com o último nó do heap. Em seguida, reajuste o número anterior (n-1) para torná-lo uma pilha. E assim por diante, até que haja apenas dois nós no heap, troque-os e, finalmente, obtenha uma sequência ordenada de n nós. Do ponto de vista da descrição do algoritmo, a classificação do heap requer dois processos, um é construir um heap e o outro é trocar posições entre o topo do heap e o último elemento do heap. Portanto, a classificação de heap tem duas funções. Uma é a função de penetração para construir um reator e a outra é uma função que chama repetidamente a função de penetração para realizar a classificação.

A dificuldade é (1) como gerar uma sequência em uma grande pilha

(2) Depois de produzir o elemento superior do heap, como fazer com que os elementos restantes gerem um grande heap raiz

Ideias:

  • Etapa 1: construir uma grande pilha de raízes - construir uma grande pilha de raízes a partir de uma sequência não ordenada de n elementos,

  • Etapa 2: Troque os elementos do heap - troque o elemento final e o elemento principal para tornar o elemento final o maior;

  • Etapa 3: reconstruir a pilha de raiz grande - ajustar a sequência desordenada composta dos primeiros n-1 elementos à pilha de raiz grande

    Repita as etapas dois e três até que toda a sequência esteja em ordem.

2. Exemplos

Exemplo 1: arr []

img

Fonte da imagem: https://www.cnblogs.com/zwtgyh/p/10631760.html

#include<iostream>
#include<vector>
using namespace std;

// 递归方式构建大根堆(len是arr的长度,index是第一个非叶子节点的下标)
void adjust(vector<int> &arr, int len, int index)
{
	int left = 2 * index + 1; // index的左子节点
	int right = 2 * index + 2;// index的右子节点

	int maxIdx = index;
	if (left<len && arr[left] > arr[maxIdx])     maxIdx = left;
	if (right<len && arr[right] > arr[maxIdx])     maxIdx = right;

	if (maxIdx != index)
	{
		swap(arr[maxIdx], arr[index]);
		adjust(arr, len, maxIdx);
	}

}

// 堆排序
void heapSort(vector<int> &arr, int size)
{
	// 构建大根堆(从最后一个非叶子节点向上)
	for (int i = size / 2 - 1; i >= 0; i--)
	{
		adjust(arr, size, i);
	}

	// 调整大根堆
	for (int i = size - 1; i >= 1; i--)
	{
		swap(arr[0], arr[i]);           // 将当前最大的放置到数组末尾
		adjust(arr, i, 0);              // 将未完成排序的部分继续进行堆排序
	}
}

int main()
{
	vector<int> arr = { 8,6,7,4,5,3,2,1 };
	heapSort(arr, arr.size());
	for (int i = 0; i<arr.size(); i++)
	{
		cout << arr[i] <<"  ";
	}
	cout << endl;
	return 0;
}

Exemplo 2: arr [4,6,8,5,9] classificar por classificação de heap

  • Etapa 1: construir um grande heap de raiz

① Seqüência não ordenada para construir uma árvore binária completa

imagem

② Do último nó de folha, ajuste da esquerda para a direita, de baixo para cima e ajuste a árvore binária completa para uma grande pilha de raízes

a. Encontre o primeiro nó não folha 6, porque o nó filho direito de 6 é maior que 6, então troque 6 e 9. Após a troca, ele se adapta à estrutura da grande pilha de raízes.

imagem

c. Encontre o segundo nó não folha 4, uma vez que o nó filho esquerdo de 4 é maior que 4, então troque 4 por 9. Depois que a troca não estiver de acordo com a estrutura da pilha de raízes grandes, continue a ajustar da direita para a esquerda e de baixo para cima.

imagem

imagem

  • Etapa 2: troque os elementos do heap (troque os elementos da cabeça e da cauda - obtenha o maior elemento)

imagem

  • Etapa 3: reconstruir a grande pilha de raízes (os primeiros n-1 elementos)

imagem

  • Repita as etapas 2 e 3 até que toda a sequência esteja em ordem

imagem

Fonte da imagem: https://www.cnblogs.com/chengxiao/p/6129630.html

Código de exemplo:

#include<iostream>
#include<vector>
using namespace std;

// 递归方式构建大根堆(len是arr的长度,index是第一个非叶子节点的下标)
void adjust(vector<int> &arr, int len, int index)
{
	int left = 2 * index + 1; // index的左子节点
	int right = 2 * index + 2;// index的右子节点

	int maxIdx = index;
	if (left<len && arr[left] > arr[maxIdx])     maxIdx = left;
	if (right<len && arr[right] > arr[maxIdx])     maxIdx = right;

	if (maxIdx != index)
	{
		swap(arr[maxIdx], arr[index]);
		adjust(arr, len, maxIdx);
	}

}

// 堆排序
void heapSort(vector<int> &arr, int size)
{
	// 构建大根堆(从最后一个非叶子节点向上)
	for (int i = size / 2 - 1; i >= 0; i--)
	{
		adjust(arr, size, i);
	}

	// 调整大根堆
	for (int i = size - 1; i >= 1; i--)
	{
		swap(arr[0], arr[i]);           // 将当前最大的放置到数组末尾
		adjust(arr, i, 0);              // 将未完成排序的部分继续进行堆排序
	}
}

int main()
{
	vector<int> arr = { 4, 6, 8, 5, 9 };
	heapSort(arr, arr.size());
	for (int i = 0; i<arr.size(); i++)
	{
		cout << arr[i] <<"  ";
	}
	cout << endl;
	return 0;
}

3. Resumo

  • A complexidade de tempo da classificação do heap é composta principalmente de duas partes: o processo de inicializar o heap e reconstruir o heap depois de retirar o elemento superior a cada vez
  • A complexidade de tempo do processo de inicialização da construção de um heap é O (n): Supondo que a altura do heap seja k, começando no nó à direita da penúltima camada, os nós desta camada devem comparar os nós filhos e escolher se deseja trocar, o terceiro ao último As camadas são semelhantes, em todo o caminho até a primeira camada (ou seja, o número de camadas é de k-1 a 1); então, o tempo total é (2 (i-1)) * (ki), onde i representa a i-ésima camada (o intervalo é k-1) Para 1), 2 (i-1) indica quantos elementos estão na camada, (ki) indica o número de comparações na subárvore, ou seja, S = 2 ^ (k-2) * 1 + 2 ^ (k-3) 2 + 2 ^ (k-4) 3 +… + 2 ^ 1 (k-2) + 2 ^ 0 (k-1), usando subtração escalonada (use a constante 2 para auxiliar na conversão, multiplique ambos os lados por 2 e subtraia Remova a equação original) para obter S = 2 ^ (K-1) + 2 ^ (K-2) + 2 ^ (K-3) +… + 2- (K-1), ignorando o último termo constante está apenas esperando A sequência de razão, isto é, S = 2 k-2- (k-1) = 2 kk-1, e como k é a profundidade de uma árvore binária completa, então 2 ^ k <= n <2 ^ k-1, podemos considerar k = logn, em resumo, S = n-logn -1, então a complexidade do tempo é O (n)
  • A complexidade de tempo do processo de reconstrução do heap após estourar o elemento superior do heap é O (nlogn): loop n-1 vezes, cada vez do nó seguinte para a pesquisa de loop, então cada vez que é logn, o tempo total é (n-1) * logn = nlogn-logn
  • Portanto, a complexidade de tempo de classificação de heap é O (n) + O (nlogn) = O (nlogn)
  • A classificação por heap é uma classificação aterrada, então a complexidade do espaço é constante O (1)

Acho que você gosta

Origin blog.csdn.net/qq_43801020/article/details/108136124
Recomendado
Clasificación