Estructura de datos: aplicación del montón (ordenación del montón y problema topk)

inserte la descripción de la imagen aquí

Página de inicio personal: Página de inicio personal
Columna personal: "Estructura de datos" "Lenguaje C"


clasificación de montón

La clasificación del montón consiste en crear primero un montón de datos y luego utilizar la idea de eliminación del montón para ordenar.

  1. Construya un montón de la matriz a ordenar
  2. Intercambie los datos en la parte superior del montón con los datos al final de la matriz
  3. Ajuste los nuevos datos superiores del montón para que la estructura del montón permanezca sin cambios

Repita los pasos 2 y 3 hasta que no haya datos en el montón.

construir pila

  • Construya un pequeño montón en orden descendente (el nodo principal es menor o igual que el nodo secundario)
  • Cree un montón grande en orden ascendente (el nodo principal es mayor o igual que el nodo secundario)

Hay dos formas de construir un montón: construir un montón y construir un montón hacia abajo. Entre ellos, construir una pila hacia abajo es mejor que construir una pila hacia arriba.

Construya el montón hacia abajo: comenzando desde el nodo principal del último nodo secundario, atraviesa la matriz que se ordenará hacia adelante y se ajusta continuamente hacia abajo.
De la siguiente manera: cree un pequeño montón para la matriz {16, 72, 31, 94, 53, 23} ¿
inserte la descripción de la imagen aquí
Por qué no puede comenzar desde el primer elemento de la matriz? Porque la premisa del ajuste descendente es que el subárbol izquierdo y el subárbol derecho del nodo raíz son montones grandes o pequeños. Los árboles vacíos y los árboles con un solo nodo pueden ser montones grandes o pequeños.

Clasificación de pensamientos de eliminación de montón

  • Intercambie la parte superior del montón con el final de la matriz sin clasificar
  • Ajuste los nuevos datos superiores del montón hacia abajo para garantizar que la estructura del montón permanezca sin cambios
  • Intercambie los datos al final de la nueva matriz sin clasificar con los datos en la parte superior del nuevo montón

Repita los pasos anteriores para completar la clasificación.
También puede explicar por qué los montones grandes se construyen en orden ascendente y los pequeños en orden descendente. Los datos superiores del montón pequeño son siempre los datos más pequeños del montón, intercambie los datos superiores con la cola de la matriz sin clasificar y repita los pasos anteriores. El dato más pequeño es el último elemento de la matriz, y el segundo dato más pequeño es el penúltimo elemento del número... Entonces se completa el orden descendente.

como sigue

inserte la descripción de la imagen aquí

Código

//向下调整 小堆,假设该节点是 i, 右孩子节点是 2 * i + 1,左孩子节点是 2 * i + 2
void AdjustDown(HPDataType* data, int parent, int size)
{
    
    
	int child = parent * 2 + 1;

	while (parent < size)
	{
    
    
		//防止越界                    找左右孩子中最小的
		if (child + 1 < size && data[child] > data[child + 1])
		{
    
    
			child++;
		}

		if (child < size && data[parent] > data[child])
		{
    
    
			swap(&data[parent], &data[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
    
    
			break;
		}
	}
}


// 对数组进行堆排序
//先构建堆    升序:大堆     降序:小堆
//如降序,先建小堆,再将堆顶数据放入数组尾部,从新选择堆顶数据
void HeapSort(int* a, int n)
{
    
    
	建堆
	向上建堆   类似于插入数据
	//for (int i = 0; i < n; i++)
	//{
    
    
	//	AdjustUp(a, i);
	//}

	//向下建堆   向下调整的前提:该节点的左右子树要都是大堆或小堆
	//倒着从第一个非叶子结点开始向下建堆
	//             n 是数据个数 n-1 是数组最后一个元素   (子节点 - 1) / 2 == 父节点
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
    
    
		AdjustDown(a, i, n);
	}


	//将堆顶数据交换数组尾部数据,再选新的堆顶,再交换新的数组尾
	int end = n - 1;
	while (end > 0)
	{
    
    
		swap(&a[0], &a[end]);
		AdjustDown(a, 0, end);
		end--;
	}
}

int main()
{
    
    
	int arr[] = {
    
     16, 72, 31, 23, 94, 53 };
	int size = sizeof(arr) / sizeof(arr[0]);

	HeapSort(arr, size);
	for (int i = 0; i < size; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	printf("\n");
}

k preguntas principales

El problema de los k principales es seleccionar los K números principales de los N números (N es mucho mayor que K)
de la siguiente manera: creamos aleatoriamente 10,000 números menores que 1,000,000 y encontramos los 5 números más grandes a partir de ellos.

tren de pensamiento

Primero podemos construir un pequeño montón con los primeros 5 números y luego atravesar 9995 números. Si el número es mayor que el número en la parte superior del montón, reemplace el número con el número en la parte superior del montón y luego ajuste hacia abajo para asegurar la estructura del montón pequeño y continúe atravesando el resto. Cuente hasta atravesar 9995 números. Entonces los 5 números del montón son los 5 números más grandes de 10000.

Código

¿Cómo comprobar la exactitud del código?
Primero podemos ejecutar el código para crear datos y luego reescribir aleatoriamente 5 números en el archivo creado para hacerlo mayor que 1.000.000. Luego podemos proteger la función de creación de datos para ejecutar la función PrintTopK.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void CreateNDate()
{
    
    
	// 造数据
	int n = 10000;
	srand((unsigned)time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
    
    
		perror("fopen error");
		return;
	}

	for (int i = 0; i < n; ++i)
	{
    
    
		int x = rand() % 1000000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}




//从N个数中选处最大的K个数
//用前K个数建小堆(向下调整 or 向上调整),遍历N - K 个数,  (如果是大堆,那么有可能堆顶数据在一开始就是 N 个数中最大的)
//如果该数大于堆顶数据,堆顶数据 与 该数 交换在向下调整。
//遍历完 N - K 个数,那么堆中数据就是 N 个数中最大的 K 个数

void swap(int* a, int* b)
{
    
    
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

//小堆  父节点小于等于子节点
void AdjustDown(int* data, int parent, int size)
{
    
    
	int child = parent * 2 + 1;

	while (parent < size)
	{
    
    

		if (child + 1 < size && data[child] > data[child + 1])
		{
    
    
			child++;
		}

		if (child < size && data[parent] > data[child])
		{
    
    
			swap(&data[child], &data[parent]);

			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
    
    
			break;
		}
	}
}

void PrintTopK(int k)
{
    
    
	const char* file = "data.txt";
	FILE* fin = fopen(file, "r");

	//读取前K个数据
	int* ans = (int*)malloc(sizeof(int) * (k + 1));
	if (ans == NULL)
	{
    
    
		perror("malloc:");
		exit(-1);
	}

	for (int i = 0; i < k; i++)
	{
    
    
		fscanf(fin, "%d", &ans[i]);
	}

	//建堆
	for (int i = (k - 1) / 2; i >= 0; i--)
	{
    
    
		AdjustDown(ans, i, k);
	}

	while (!feof(fin))
	{
    
    	
		//读取数据
		int val = 0;
		fscanf(fin, "%d", &val);

		if (val > ans[0])
		{
    
    
			swap(&val, &ans[0]);
			AdjustDown(ans, 0, k);
		}
	}

	
	//打印数据
	for (int i = 0; i < k; i++)
	{
    
    
		printf("%d ", ans[i]);
	}
	printf("\n");
}


int main()
{
    
    
	CreateNDate();

	int k = 0;
	scanf("%d", &k);
	PrintTopK(k);
	return 0;
}

Resumir

¡Lo anterior es mi comprensión de la aplicación del montón! ! !
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/li209779/article/details/132236113
Recomendado
Clasificación