Problème TopK de tri de tas

1. Implémentation d'interfaces de fonctions liées au tas

insérez la description de l'image ici

Le tas est un arbre binaire complet, qui est divisé en deux structures : grand tas et petit tas. Grand
tas : tout nœud parent est supérieur ou égal au nœud enfant. L'image ci-dessus est un grand tas.
Petit tas : tout parent nœud est inférieur ou égal au nœud enfant
dans l'ordre Quelle est la relation entre les indices ?
leftchild=parent * 2+1
rightchild=parent * 2+2
Ensuite, vous pouvez également obtenir
la table suivante du nœud parent est (indice du nœud enfant -1)/2
utilise principalement la table de séquence pour stocker le tas. Cette structure de données
complète principalement ce qui suit l'interface fonctionnelle de

#pragma once

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

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

void HeapInit(Heap* php);
void HeapDestroy(Heap* php);
void HeapPush(Heap* php, HPDataType x);
void HeapPop(Heap* php);
HPDataType HeapTop(Heap* php);
bool HeapEmpty(Heap* php);
int HeapSize(Heap* php);
void  HeapCreate(Heap* php,HPDataType* a, int size);
void AdjustDown(HPDataType* a, int size, int parent);
void AdjustUp(HPDataType* a, int child);

1. Initialisation du tas

L'initialisation est très simple consiste à initialiser la table de séquence utilisée pour stocker le tas

void HeapInit(Heap* php)
{
    
    
	assert(php);
	php->a = NULL;
	php->capcity = 0;
	php->size = 0;
}

2. Destruction du tas

void HeapDestroy(Heap* php)
{
    
    
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capcity = 0;
	php->size = 0;
}

3. Insérer

insérez la description de l'image ici
Maintenant, pour insérer un nœud, il doit être lié à la position du sous-arbre gauche de 6.
Si la valeur du nœud inséré est inférieure à 6, cela n'affectera pas la structure du tas, et il s'agit toujours d'un gros tas.
Si la valeur du nœud inséré est supérieure à 6, alors la structure du tas sera affectée à ce moment, ce qui en fera un tas pas grand, nous allons donc ajuster ce nœud vers le haut. Et pendant le processus d'ajustement, seule la position relative du nœud à ajuster et de son nœud parent sera modifiée jusqu'à ce qu'il soit conforme à la structure du tas.
insérez la description de l'image ici
Utilisons d'abord des diagrammes pour montrer le processus d'ajustement suivant : ajustez vers le haut tour à tour
insérez la description de l'image ici
et déterminez si une expansion est nécessaire avant l'insertion.

void HeapPush(Heap* php, HPDataType x)
{
    
    
	assert(php);
	if (php->capcity == php->size)
	{
    
    
		int newcapcity = (php->capcity == 0 ? 4 : php->capcity * 2);
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newcapcity);
		if (!tmp)
		{
    
    
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capcity = newcapcity;
	}
	php->a[php->size] = x;
	php->size++;
	AdjustUp(php->a, php->size - 1);
}

4. Ajustez vers le haut

void AdjustUp(HPDataType* a, int child)
{
    
    
	int parent = (child - 1) / 2;
	while (child > 0)
	{
    
    
		if (a[child] > a[parent])
		{
    
    
			swep(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
    
    
			break;
		}
	}
}

5. Supprimer

Lors de la suppression d'un nœud, généralement le nœud en haut du tas est supprimé, car si le tas est grand, les données en haut du tas sont les plus grandes, sinon ce sont les plus petites, et la suppression d'autres nœuds n'a pas de sens.
Il existe également des astuces lors de la suppression. Si nous déplaçons directement les données suivantes vers l'avant pour écraser les premières données, l'ensemble du tas deviendra désordonné, nous utilisons donc cette méthode : échangez d'abord l'élément du haut avec le dernier élément, puis taillez - À
ce
moment , le sous-arbre gauche et le sous-arbre droit en haut du tas doivent toujours être un grand tas et n'ont pas été détruits, nous devons donc ajuster vers le bas, comme pour l'ajustement vers le haut, jusqu'à ce qu'il devienne une grande structure de tas.

Portez une attention particulière lors de l'ajustement vers le bas. Si le nœud parent est plus petit que le nœud enfant, alors le nœud parent doit être échangé avec le plus grand des nœuds enfants gauche et droit.

insérez la description de l'image ici

void HeapPop(Heap* php)
{
    
    
	assert(php);
	assert(!HeapEmpty(php));
	//首尾交换
	swep(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	//向下调整
	AdjustDown(php->a, php->size, 0);
}

6. Ajustez vers le bas

void AdjustDown(HPDataType* a, int size, int parent)
{
    
    
	int child = parent * 2 + 1;
	while (child < size)
	{
    
    
		//如果右子树大于左子树那么孩子结点就选右子树
		if (child + 1 < size && a[child + 1] > a[child])
		{
    
    
			child++;
		}
		if (a[child] > a[parent])
		{
    
    
			swep(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
    
    
			break;
		}
	}
}

7. Construisez des tas

Le mode de construction de tas ci-dessus consiste à insérer et à construire des tas un par un, et l'efficacité est relativement faible.
Permettez-moi de présenter deux méthodes de construction de tas, qui sont basées sur un ajustement vers le haut et un ajustement vers le bas.

La première consiste à ajuster la pile vers le haut
et la seconde consiste à ajuster la pile vers le bas

Ajustez vers le haut pour créer un tas, basé sur le fait qu'avant d'insérer ces données, il s'agissait à l'origine d'un grand tas .
Étant donné un tableau, commencez à ajuster vers le haut à partir de l'indice 1 (car lorsqu'il n'y a qu'un seul élément, c'est un grand tas ou un petit tas), jusqu'au dernier élément.

L'ajustement vers le bas du tas est basé sur le fait que les mots de gauche et de droite sont tous de grandes piles
insérez la description de l'image ici
, comme le montre la figure ci-dessus. Pour 6, les sous-arbres de gauche et de droite ne sont pas de grandes piles, nous ne pouvons donc pas commencer à ajuster à partir de 6, mais nous pouvons aller de bas en haut. Le nombre de mots à gauche et à droite de 5 est important, donc ajustez d'abord 5, puis ajustez 9 et 6 tour à tour.

void  HeapCreate(Heap* php, HPDataType* a, int size)
{
    
    
	assert(php);
	php->a = (HPDataType*)malloc(sizeof(HPDataType) * size);
	if (!php->a)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	php->capcity = size;
	php->size = size;
	memcpy(php->a, a, sizeof(HPDataType) * size);
	//向上调整建堆
	/*for (int i = 1; i < size; i++)
	{
		AdjustUp(php->a, i);
	}*/
	//向下调整建堆
	for (int i = (size - 1 - 1) / 2; i >= 0; i--)
	{
    
    
		AdjustDown(php->a, size, i);
	}
}

8. Prenez le dessus de la pile

HPDataType HeapTop(Heap* php)
{
    
    
	assert(php);
	assert(!HeapEmpty(php));
	return php->a[0];
}

9. Jugement vide

bool HeapEmpty(Heap* php)
{
    
    
	assert(php);
	return php->size == 0;
}

10, la taille du tas

int HeapSize(Heap* php)
{
    
    
	assert(php);
	return php->size;
}

Deuxièmement, la complexité temporelle de la constitution du tas et de la réduction du tas

Les deux méthodes de construction de tas sont présentées ci-dessus, alors quelle méthode devons-nous utiliser ?
Ce doit être celui qui a un rendement élevé, calculons donc maintenant la complexité temporelle des deux méthodes suivantes

ajuster
insérez la description de l'image ici

ajuster
insérez la description de l'image ici

On peut voir qu'un ajustement vers le bas de la construction de pieux est meilleur qu'un ajustement vers le haut de la construction de pieux.

Trois, tri en tas

L'idée du tri de tas est d'ajuster le tas vers le bas en fonction du tableau donné (construire un grand tas dans l'ordre croissant et construire un petit tas dans l'ordre décroissant).En prenant l'ordre croissant comme exemple, une fois le tas construit avec
succès , l'élément supérieur du tas est la plus grande donnée du tableau. Échangez avec les dernières données du tas, de sorte que les données les plus volumineuses soient placées à la fin, puis taillez--réduisez les données du tas d'une unité, donc que les opérations suivantes n'affecteront pas les plus grandes données sélectionnées à l'étape précédente, puis ajuster la construction vers le bas La pile est alors exploitée séquentiellement.
insérez la description de l'image ici

void HeapSort(int* a, int size)
{
    
    
	//升序建大堆
	for (int i = (size - 2) / 2; i >= 0; i--)
	{
    
    
		AdjustDown(a, size, i);
	}
	int end = size - 1;
	while (end > 0)
	{
    
    
		swep(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

La formule de calcul de la complexité temporelle du tri en tas est similaire à la formule d'ajustement à la hausse,
complexité temporelle : O(N*logN) complexité spatiale O(1)

Quatre, problème TopK

Il y a deux façons de penser au problème TopK :
nous prenons la sélection du plus grand nombre K comme exemple
1, construisons un grand tas de toutes les données, prenons le haut du tas à chaque fois, supprimons le haut du tas, et régler vers le bas.
Cependant, l'inconvénient de cette idée est que lorsque la quantité de données fournies est énorme et ne peut pas être stockée dans la mémoire, elle ne pourra pas fonctionner.
2. Créez un petit tas avec des nœuds K. Étant donné que le haut du petit tas est la plus petite donnée, puis parcourez les données entières tant qu'elles sont plus grandes que le haut du tas, puis entrez dans le tas, puis ajustez vers le bas jusqu'à ce que le parcours est complet, le plus grand K Les nombres sont tous dans le tas.
Cette façon de penser n'a pas besoin de se soucier du problème de mémoire, et les données peuvent être lues à partir du fichier si la quantité de données est importante.
Alors laissez-moi vous expliquer l'idée

void TopK(int* a, int size, int k)
{
    
    
	int* minHeap = (int*)malloc(sizeof(int) * k);
	if (!minHeap)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	int j = 0;
	for (j = 0; j < k; j++)
	{
    
    
		minHeap[j] = a[j];
	}
	//建k个结点的堆
	for (int i = (k - 2) / 2; i >= 0; i--)
	{
    
    
		AdjustDown(minHeap, k, i);
	}
	for (; j < size; j++)
	{
    
    
		if (a[j] > minHeap[0])
		{
    
    
			minHeap[0] = a[j];
			AdjustDown(minHeap, k, 0);
		}
	}
	for (int i = 0; i < k; i++)
	{
    
    
		printf("%d ", minHeap[i]);
	}
	
}

Complexité temporelle de l'idée 2 : O (N*logK)
Complexité temporelle de l'idée 1 : O (N)

Je suppose que tu aimes

Origine blog.csdn.net/Djsnxbjans/article/details/128049964
conseillé
Classement