[Estructura de datos: lenguaje C] Montón, eres un montón, mira cómo puedo manejarte a fondo

Tabla de contenido

1. El concepto y la estructura del montón.

1.1 Conceptos (los conceptos siempre son importantes)

1.2 Estructura, dividida en dos tipos

1.2.1 Ejemplo de montón pequeño/montón raíz pequeño

1.2.2 Ejemplo de gran montón/gran montón raíz

2. Interfaz de montón

3. Implementación de la interfaz

3.1 Inicialización del montón

3.2 Destrucción del montón

3.3 Inserción de montón

Análisis funcional:

Realización de funciones:

3.4 Eliminación de montones

Análisis funcional:

Realización de funciones:

3.5 Obtenga los datos en la parte superior del montón

3.6 El número de datos en el montón

3.7 Sentencia de montón vacío

4. Código completo


1. El concepto y la estructura del montón.

1.1 Conceptos (los conceptos siempre son importantes)

El párrafo anterior es el concepto de montones, pero esto es demasiado aburrido, hablemos de una manera simple, golpeando la pizarra:

La esencia del montón es un árbol binario completo.

Montón grande (también llamado montón raíz grande): el nodo principal es mayor o igual que el nodo secundario.

Par pequeño (también llamado montón raíz pequeño): el nodo principal es menor o igual que el nodo secundario.

Si no se cumplen las condiciones anteriores, entonces no es un montón.

Propiedades del montón :

1. El valor de un nodo en el montón siempre no es mayor ni menor que el valor de su nodo padre
2. El montón es siempre un árbol binario completo.

1.2 Estructura, dividida en dos tipos

1.2.1 Ejemplo de montón pequeño/montón raíz pequeño

1.2.2 Ejemplo de gran montón/gran montón raíz

Veamos un tema:

La siguiente secuencia de palabras clave es un montón: (A)

100,60,70,50,32,65 _

B60,70,65,50,32,100 _

C65,100,70,32,50,60 _

D 70,65,100,32,50,60

E 32,50,100,70,65,60

F 50,100,70,65,60,32

Análisis : Dibujamos un gráfico para analizar

2. Interfaz de montón

Este artículo se implementa utilizando el montón pequeño como ejemplo. El almacenamiento de datos del montón se almacena en una matriz, y la estructura de almacenamiento de los datos en la memoria se almacena secuencialmente. Lo entendemos en una estructura lógica para facilitar la comprensión.

Las interfaces del montón incluyen: inicialización, destrucción, inserción, eliminación, tomar la parte superior del montón, la cantidad de datos en el montón y el juicio vacío.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;

// 堆的初始化
void HeapInit(Heap* hp);
// 堆的销毁
void HeapDestory(Heap* hp);
//交换
void Swap(HPDataType* p1, HPDataType* p2);
//向上调整
void AdjustUp(HPDataType* a, int child);
//向下调整
void AdjustDown(HPDataType* a, int size, int parent);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
bool HeapEmpty(Heap* hp);

3. Implementación de la interfaz

Muchas de nuestras interfaces son similares a los artículos anteriores sobre estructura de datos. Se han explicado muchas veces antes, por lo que no las explicaré aquí. Si hay algo que no entiende, puede consultar los artículos anteriores sobre estructura de datos.

3.1 Inicialización del montón

void HeapInit(Heap* hp)
{
	assert(hp);

	hp->a = NULL;
	hp->size = 0;
	hp->capacity = 0;
}

3.2 Destrucción del montón

void HeapDestory(Heap* hp)
{
	assert(hp);

	free(hp->a);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}

3.3 Inserción de montón

La inserción del montón es más complicada y difícil. Analicémoslo primero y luego implementemos la función.

Análisis funcional:

1. Al insertar, primero debemos verificar si la matriz debe expandirse, primero determinar si está llena, si el espacio está lleno, primero expanda la capacidad y luego inserte el nuevo elemento al final de la matriz;

2. Cuando insertamos un nuevo elemento, debemos analizar si el montón cumple con la estructura del montón pequeño, si no, debemos ajustar el nuevo elemento hacia arriba.

3. Análisis del proceso de ajuste al alza:

Tomemos un ejemplo para analizar: si la estructura del montón se destruye después de insertar un elemento en un montón pequeño, cómo ajustar para restaurar la estructura del montón pequeño.

a.Cuando encontramos que después de insertar un 10 en el montón pequeño, 10 es más pequeño que el nodo principal 28, lo que destruye la estructura del montón pequeño, y necesitamos ajustar el montón;

b. La estructura física del montón es una matriz, por lo que podemos encontrar el nodo principal a través del subíndice. Aquí está la fórmula para encontrar el nodo principal: padre = (hijo-1)/2. Cuando encontremos el nodo principal, compare el nodo secundario con el nodo principal. Si es más pequeño que el nodo principal, intercambiamos los elementos de los dos nodos. El nodo principal intercambiado y su nodo principal pueden no satisfacer el montón pequeño, por lo que necesitamos continuar con el ajuste al alza de

c. Bucle para comparar y ajustar, cuando child = 0, nuestro ajuste ha terminado, por lo que nuestra condición de juicio de bucle es child > 0.

Nota: después de un ajuste, nuestro montón se ha convertido en un montón pequeño y saldremos del bucle.

Hagamos un dibujo de acuerdo con las ideas anteriores:

Nuestro análisis de la función ha terminado y empezamos a darnos cuenta de la función.

Realización de funciones:

void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])//这里控制大小堆
		{
			Swap(&a[child], &a[parent]);

			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//log N 
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);

	if (hp->size == hp->capacity)
	{
		int newcapacity = hp->capacity == 0 ? 4 : 2 * hp->capacity;
		HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);
		if (NULL == tmp)
		{
			perror("realloc fail:");
		}

		hp->a = tmp;
		hp->capacity = newcapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;

	AdjustUp(hp->a, hp->size - 1);
}

Reutilizaremos el intercambio y el ajuste al alza más adelante, por lo que encapsularemos las dos funciones en una función.

3.4 Eliminación de montones

La eliminación del montón consiste en eliminar el elemento en la parte superior del montón.

Idea: intercambie los datos principales con los últimos datos, elimine los últimos datos y luego ajuste desde la parte superior del montón.

Análisis funcional:

Cuando eliminamos los datos en la parte superior del montón, no podemos eliminarlos directamente. Eliminar los datos en la parte superior del montón directamente destruirá la estructura del montón, y la complejidad de tiempo para construir el montón es demasiado alta, por lo que no es recomendado Aquí presentamos un método con una complejidad menor:

1. Primero intercambiamos los datos superiores con los últimos datos, luego eliminamos los últimos datos y finalmente ajustamos hacia abajo desde la parte superior del montón;

2. El ajuste a la baja es más complicado, analicemos y hagamos un dibujo para explicarlo:

a. En este momento, nuestro nodo principal es el nodo superior del montón. A continuación, debemos encontrar el menor de los dos nodos secundarios como el nodo secundario. La fórmula para encontrar el nodo secundario aquí es: child = parent*2 + 1;

B. Si el niño es más pequeño que el padre, cámbielo y continúe ajustándose hacia abajo, deje que el padre vaya a la posición del niño y luego calcule la posición del niño;

c. Cuando el subíndice del niño es mayor que el tamaño de la matriz, el ciclo termina y se completa todo el ajuste.

Nota: después de un ajuste, nuestro montón se ha convertido en un montón pequeño y saldremos del bucle.

Realización de funciones:

void AdjustDown(HPDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)//当child大于了数组大小就跳出循环
	{
		//找出左右孩子中小/大的那个(假设法)
		if (child + 1 < size && a[child + 1] < a[child])
		{
			child++;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[parent], &a[child]);

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

//log N
void HeapPop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));

	Swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;

	AdjustDown(hp->a, hp->size, 0);
}

3.5 Obtenga los datos en la parte superior del montón

HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));

	return hp->a[0];
}

3.6 El número de datos en el montón

int HeapSize(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));

	return hp->size;
}

3.7 Sentencia de montón vacío

bool HeapEmpty(Heap* hp)
{
	assert(hp);

	return hp->size == 0;
}

4. Código completo

El código completo está en el almacén de códigos: Heap Xiaobai está trabajando duro en jy/DataStructure - Code Cloud - Open Source China (gitee.com)

Supongo que te gusta

Origin blog.csdn.net/Ljy_cx_21_4_3/article/details/130903751
Recomendado
Clasificación