Estructura de datos: algoritmo de clasificación común (5): clasificación de montón (implementación de C ++)

Estructura de datos: algoritmo de clasificación común (5): clasificación de montón

La clasificación de montón es una especie de clasificación de selección de árbol, que es una mejora eficaz para la clasificación de selección directa. Heap es una estructura de datos de árbol especial, es decir, un árbol binario completo. El montón se divide en un montón raíz grande y un montón raíz pequeño. El montón raíz grande significa que el valor del nodo raíz es mayor que el valor de los dos nodos secundarios; el montón raíz pequeño significa que el valor del nodo raíz es menor que el valor de los dos nodos secundarios, y los dos subárboles del nodo raíz también son uno. montón.

La definición de un montón: una secuencia con n elementos (h1, h2, ..., hn), si y solo si (hi> = h2i, hi> = 2i + 1) o (hi <= h2i, hi <= 2i +1) (i = 1,2, ..., n / 2) se llama montón. Aquí solo se analiza el montón que cumple las condiciones anteriores. Se puede ver en la definición del montón que el elemento superior (es decir, el primer elemento) debe ser el elemento más grande (montón superior grande). Un árbol binario completo puede representar intuitivamente la estructura del montón. La parte superior del montón es la raíz y los otros son los subárboles izquierdo y derecho. (Se puede extender al recorrido de preorden, recorrido de orden medio y recorrido de post-orden)

1. Idea básica :

Inicialmente, la secuencia de números a ordenar se considera un árbol binario almacenado secuencialmente y su orden de almacenamiento se ajusta para convertirlo en un montón. En este momento, el número de nodos raíz del montón es el mayor. Luego, intercambie el nodo raíz con el último nodo del montón. Luego, reajuste el número anterior (n-1) para convertirlo en una pila. Y así sucesivamente, hasta que solo haya dos nodos en el montón, intercambielos y finalmente obtenga una secuencia ordenada de n nodos. Desde el punto de vista de la descripción del algoritmo, la clasificación del montón requiere dos procesos, uno es construir un montón y el otro es intercambiar posiciones entre la parte superior del montón y el último elemento del montón. Entonces, la ordenación en pila tiene dos funciones. Una es la función de penetración para construir un reactor y la otra es una función que llama repetidamente a la función de penetración para lograr la clasificación.

La dificultad es (1) cómo generar una secuencia en un gran montón

(2) Después de generar el elemento superior del montón, cómo hacer que los elementos restantes generen un gran montón raíz

Ideas:

  • Paso 1: Construya una gran pila de raíces: construya una gran pila de raíces a partir de una secuencia desordenada de n elementos,

  • Paso 2: intercambie los elementos del montón: intercambie el elemento final y el elemento principal para que el elemento final sea el elemento más grande;

  • Paso 3: Reconstruya el gran pilote de raíces: ajuste la secuencia desordenada compuesta por los primeros n-1 elementos al gran pilote de raíces

    Repita los pasos dos y tres hasta que toda la secuencia esté en orden.

2. Ejemplos

Ejemplo 1: arr []

img

Fuente de la imagen: 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;
}

Ejemplo 2: arr [4,6,8,5,9] ordenar por ordenación del montón

  • Paso 1: construye un gran montón raíz

① Secuencia desordenada para construir un árbol binario completo

imagen

② Desde el último nodo de la hoja, ajuste de izquierda a derecha, de abajo hacia arriba, y ajuste el árbol binario completo a una pila de raíces grande

a. Encuentre el primer nodo no hoja 6, porque el nodo hijo derecho de 6 es mayor que 6, así que intercambie 6 y 9. Después del intercambio, se ajusta a la estructura del gran pilote de raíces.

imagen

c. Encuentre el segundo nodo no hoja 4, ya que el nodo hijo izquierdo de 4 es mayor que 4, así que intercambie 4 y 9. Después de que el intercambio no se ajuste a la estructura del gran pilote de raíces, continúe ajustando de derecha a izquierda y de abajo hacia arriba.

imagen

imagen

  • Paso 2: intercambiar elementos de montón (intercambiar elementos de cabeza y cola, obtener el elemento más grande)

imagen

  • Paso 3: Reconstruya la pila de raíces grande (los primeros n-1 elementos)

imagen

  • Repita los pasos 2 y 3 hasta que toda la secuencia esté en orden

imagen

Fuente de la imagen: https://www.cnblogs.com/chengxiao/p/6129630.html

Código de ejemplo:

#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. Resumen

  • La complejidad temporal de la ordenación del montón se compone principalmente de dos partes: el proceso de inicializar el montón y reconstruir el montón después de extraer el elemento superior cada vez
  • La complejidad de tiempo del proceso de inicialización para construir un montón es O (n): Suponiendo que la altura del montón es k, comenzando desde el nodo a la derecha de la penúltima capa, los nodos de esta capa deben comparar los nodos secundarios y elegir si intercambiar, el penúltimo Las capas son similares, hasta la primera capa (es decir, el número de capas es de k-1 a 1); luego el tiempo total es (2 (i-1)) * (ki), donde i representa la i-ésima capa (el rango es k-1) Para 1), 2 (i-1) indica cuántos elementos hay en la capa, (ki) indica el número de comparaciones en el subárbol, es decir, S = 2 ^ (k-2) * 1 + 2 ^ (k-3) 2 + 2 ^ (k-4) 3 +… + 2 ^ 1 (k-2) + 2 ^ 0 (k-1), usando resta fuera de lugar (use la constante 2 para ayudar a la conversión, multiplique ambos lados por 2 y reste Quite la ecuación original) para obtener S = 2 ^ (K-1) + 2 ^ (K-2) + 2 ^ (K-3) +… + 2- (K-1), ignorar el último término constante es solo esperar La secuencia de razón, es decir, S = 2 k-2- (k-1) = 2 kk-1, y como k es la profundidad de un árbol binario completo, entonces 2 ^ k <= n <2 ^ k-1, podemos considerar k = logn, en resumen, S = n-logn -1, por lo que la complejidad del tiempo es O (n)
  • La complejidad de tiempo del proceso de reconstrucción del montón después de hacer estallar el elemento superior del montón es O (nlogn): bucle n-1 veces, cada vez desde el nodo siguiente hasta la búsqueda del bucle, por lo que cada vez es logn, el tiempo total es (n-1) * logn = nlogn-logn
  • Por lo tanto, la complejidad de tiempo de la ordenación del montón es O (n) + O (nlogn) = O (nlogn)
  • La clasificación de montón es una clasificación basada en tierra, por lo que la complejidad del espacio es constante O (1)

Supongo que te gusta

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