[Estructura de datos y algoritmo] Heap Sort

ordenar en montón

La cola de prioridad (priority queue) es una cola organizada de acuerdo con una determinada prioridad. Los elementos con mayor prioridad se eliminarán antes y los elementos con la misma prioridad se procesarán de acuerdo con el principio de primero en entrar, primero en salir. El algoritmo básico de la cola de prioridad se puede modificar sobre la base de la cola ordinaria. Por ejemplo, al ingresar a la cola, inserte el elemento al final de la cola y busque el elemento con la prioridad más alta cuando elimine la cola; o inserte el elemento en la posición adecuada de acuerdo con la prioridad al ingresar a la cola y elimine el elemento de la cola. elemento a la cabeza de la cola al quitar la cola. Estos dos métodos de implementación, enqueue o dequeue siempre tienen una complejidad de tiempo de O ( n ) {O(n)}O ( n ) . Y usando el montón para implementar la cola de prioridad, la complejidad del tiempo para entrar y salir de la cola esO (log 2 n) {O(log_{2}n)}O ( log _ _2n )

definición de montón

Un montón se puede definir como un árbol binario cuyos nodos contienen claves (una clave por nodo) y cumplen las dos condiciones siguientes:

(1) La propiedad de forma del árbol requiere que el árbol binario esté esencialmente completo (o simplemente llamado árbol binario completo), lo que significa que cada capa del árbol está llena, excepto la última capa. Es posible que falten elementos a la derecha .

(2) Requisitos de dominación de los padres, también conocidos como propiedades de almacenamiento dinámico, la clave de cada nodo debe ser mayor o igual que la clave de sus hijos (gran montón raíz), o la clave de cada nodo debe ser menor o igual que el claves de sus hijos (pequeño montón de raíz) (para cualquier hoja, consideramos que esta condición se cumple automáticamente).

Si el montón se numera de 0 a n según la secuencia, se cumple la siguiente relación entre los nodos:

inserte la descripción de la imagen aquí

A partir de la definición de un montón, se puede ver que si un árbol binario completo es un montón, el nodo raíz (llamado la parte superior del montón) debe ser el más grande (gran montón raíz) o el más pequeño (pequeño montón raíz) de todos los nodos. en el montón actual, como se muestra en la siguiente figura Mostrar. Una cola de prioridad implementada con un montón raíz pequeño se denomina cola mínima, y ​​una cola de prioridad implementada con un montón raíz grande se denomina cola máxima.

inserte la descripción de la imagen aquí

construccion de montones

La estructura lógica del montón es un árbol binario completo y la estructura de almacenamiento del montón generalmente se almacena en una matriz. Tomando el montón raíz grande como ejemplo, el montón puede definirse como una matriz H [ 0.. n ] H[0..n]H [ 0.. n ] , donde, en la primera mitad de la matriz, el elemento en cada posición i siempre es mayor o igual que el elemento en la posición 2i+1 y 2i+2, es decir, existe la siguiente relación : H [ yo ] ≥
max { H [ 2 yo + 1 ] , H [ 2 yo + 2 ] } yo = 0... ⌊ ( norte + 1 ) / 2 − 1 ⌋ H[i] \ge max\{ H[2i+1], H[2i+2]\} \\ i=0...\lpiso{(n+1)/2-1}\rpisoH [ yo ]ma x { H [ 2 yo+1 ] ,H [ 2i _+2 ]}i=0... ( norte+1 ) /21
De acuerdo con esta característica, existen dos métodos principales para construir un montón:

  1. Construcción de montón de abajo hacia arriba
  2. construcción de montón de arriba hacia abajo

Construcción de montón de abajo hacia arriba

Al inicializar un árbol binario completo que contiene n nodos, colocamos las claves en el orden dado y luego "amontonamos" el árbol de la siguiente manera. Comenzando desde el último nodo principal y subiendo hasta la raíz, el algoritmo verifica si las claves de estos nodos satisfacen el requisito de dominancia principal . Si el nodo no está satisfecho, el algoritmo intercambia la clave K del nodo con la clave más grande (clave mínima) de sus hijos y luego verifica si K cumple con el requisito de dominio parental en la nueva posición. Este proceso continúa hasta que se satisface el requisito de dominancia parental sobre K (eventualmente debe cumplirse, ya que esta condición se cumple automáticamente para las claves en cada hoja). Para el subárbol enraizado en el nodo principal actual, después de completar su "apilamiento", el algoritmo hace lo mismo para los predecesores inmediatos del nodo. Después de realizar esta operación en la raíz del árbol, el algoritmo se detiene.

inserte la descripción de la imagen aquí

// 堆构造
template<typename T>
void HeapBottomUp(vector<T>& H){
	int n = H.size();
	for(int i=n/2-1; i>=0; i--){
		int k=i;
		T v = H[k];
		bool heap = false;
		while(!heap && 2*k+1<n){
			int j=2*k+1;
			if(j+1<n && H[j] < H[j+1]){// 存在两个子女 
				j = j+1;
			}
			if(v>=H[j]){
				heap=true;
			}else{
				H[k]=H[j];
				k=j;
			}
		}
		H[k]=v;
	}
}

construcción de montón de arriba hacia abajo

El algoritmo de arriba hacia abajo (menos eficiente) construye un nuevo montón insertando sucesivamente nuevas claves en un montón preconstruido. Primero, se agrega un nuevo nodo que contiene la clave K después de la última hoja del montón actual. Luego filtre K a su posición adecuada de acuerdo con el siguiente método. Compara K con la clave de su padre: si esta última es mayor o igual que K, el algoritmo se detiene (la estructura ya es un montón); de lo contrario, intercambia las dos claves y compara K con su nuevo padre. Este intercambio continúa hasta que K no es mayor que su último padre, o hasta que se alcanza la raíz del árbol.

inserte la descripción de la imagen aquí

Eliminación desde la parte superior del montón

Retire la clave de la raíz de un montón. La clave para eliminar se intercambia con la última clave, y luego "heapificamos" (ajustamos el montón) el árbol más pequeño intercambiando la nueva clave en la raíz con la clave más grande en sus hijos hasta los requisitos principales de Advantage, se muestra el proceso específico en la figura de abajo.

inserte la descripción de la imagen aquí

// 堆调整
template<typename T>
void adjustHeap(vector<T>& H, int k, int len){
	T temp = H[k];
	for(int i=2*k+1; i<len; i=2*i+1){
		if(i+1<len && H[i] < H[i+1]){
			i++;
		}
		if(H[i] > temp){
			H[k] = H[i];
			k = i;  
		}else{
			break;
		}
	}
	H[k] = temp; 
}

ordenar en montón

Heapsort (heapsort) es un importante algoritmo de clasificación inventado por JWJ Williams. El proceso de implementación de este algoritmo de dos etapas se divide en dos partes:

Paso 1 : construya un montón, es decir, construya un montón para una matriz determinada.

Paso 2 : elimine la clave más grande, luego ajuste el montón y ejecute el segundo paso en un bucle hasta que se eliminen todos los elementos.

El resultado final es que los elementos de la matriz se eliminan en orden descendente. Pero para la implementación de matriz del montón, un elemento que se elimina es el último, por lo que la matriz resultante será exactamente la matriz original en orden ascendente.

inserte la descripción de la imagen aquí

El proceso de construcción del montón en el primer paso de la figura utiliza un método de construcción de abajo hacia arriba para construir la matriz en un gran montón raíz.

inserte la descripción de la imagen aquí

En el segundo paso de la figura, cada vez que se intercambia la parte superior del montón con el último elemento (eliminación de la clave más grande), y la parte restante (elementos que no se han eliminado antes) se ajusta al montón. de este ciclo, obtenemos Arrays en orden ascendente.

De hecho, ya sea en el peor de los casos o en el caso promedio, la eficiencia de tiempo de la clasificación del montón pertenece a O ( nlogn ) O(nlogn)O ( n registro n ) . _ _ Por lo tanto, la eficiencia de tiempo de la ordenación en montón y la eficiencia de tiempo de la ordenación en combinación pertenecen a la misma clase. Además, a diferencia de este último, heapsort está en el lugar, es decir, no requiere ningún espacio de almacenamiento adicional. Los experimentos de temporización en archivos aleatorios indican que heapsort se ejecuta más lentamente que quicksort, pero sigue siendo competitivo con mergesort.

Código

template<typename T>
void HeapSort(vector<T>& H){
	// 第一步:建堆 
	HeapBottomUp(H);
	// 第二步:删除最大键,并进行堆调整 
	for(int i=H.size()-1; i>0; i--){
		swap(H[0], H[i]); // C++中提供了swap函数 
		adjustHeap(H, 0, i);
	} 
}

Je suppose que tu aimes

Origine blog.csdn.net/zzy_NIC/article/details/121183809
conseillé
Classement