Structure des données: algorithme de tri commun (5): tri de tas (implémentation C ++)

Structure des données: algorithme de tri commun (5): tri par tas

Le tri en tas est une sorte de tri par sélection d'arbre, qui est une amélioration efficace du tri par sélection directe. Heap est une structure de données arborescente spéciale, c'est-à-dire un arbre binaire complet. Le tas est divisé en un grand tas racine et un petit tas racine. Le grand tas racine signifie que la valeur du nœud racine est supérieure à la valeur des deux nœuds enfants; le petit tas racine signifie que la valeur du nœud racine est inférieure à la valeur des deux nœuds enfants, et les deux sous-arbres du nœud racine sont également un tas.

La définition d'un tas: une séquence avec n éléments (h1, h2, ..., hn), si et seulement si (hi> = h2i, hi> = 2i + 1) ou (hi <= h2i, hi <= 2i +1) (i = 1,2, ..., n / 2) est appelé un tas. Seul le tas qui remplit les premières conditions est discuté ici. Il ressort de la définition du tas que l'élément supérieur (c'est-à-dire le premier élément) doit être le plus gros élément (grand tas supérieur). Un arbre binaire complet peut représenter intuitivement la structure du tas. Le haut du tas est la racine, et les autres sont les sous-arbres gauche et droit. (Il peut être étendu à la traversée de pré-ordre, de traversée d'ordre intermédiaire et de traversée de post-ordre)

1. Idée de base :

Au début, la séquence de nombres à trier est considérée comme un arbre binaire stocké séquentiellement, et leur ordre de stockage est ajusté pour en faire un tas. À ce stade, le nombre de nœuds racines du tas est le plus grand. Ensuite, échangez le nœud racine avec le dernier nœud du tas. Réajustez ensuite le nombre précédent (n-1) pour en faire une pile. Et ainsi de suite, jusqu'à ce qu'il n'y ait que deux nœuds dans le tas, et les échanger, et enfin obtenir une séquence ordonnée de n nœuds. Du point de vue de la description de l'algorithme, le tri du tas nécessite deux processus, l'un consiste à créer un tas et l'autre à échanger des positions entre le haut du tas et le dernier élément du tas. Le tri par tas a donc deux fonctions. L'une est la fonction de pénétration pour la construction d'un réacteur, et l'autre est une fonction qui appelle à plusieurs reprises la fonction de pénétration pour réaliser le tri.

La difficulté est (1) Comment générer une grande pile d'une séquence

(2) Après avoir sorti l'élément supérieur du tas, comment faire en sorte que les éléments restants génèrent un gros tas racine

Idées:

  • Étape 1: Construisez un gros tas de racines - construisez un gros tas de racines à partir d'une séquence non ordonnée de n éléments,

  • Étape 2: échangez les éléments du tas - échangez l'élément final et l'élément head, de sorte que l'élément final soit le plus grand élément;

  • Étape 3: Reconstruisez le gros tas de racines - ajustez la séquence désordonnée composée des n-1 premiers éléments au gros tas de racines

    Répétez les étapes deux et trois jusqu'à ce que toute la séquence soit dans l'ordre.

2. Exemples

Exemple 1: arr []

img

Source de l'image: 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;
}

Exemple 2: arr [4,6,8,5,9] tri par tri de tas

  • Étape 1: Créez un gros tas racine

① Séquence non ordonnée pour construire un arbre binaire complet

image

② À partir du dernier nœud feuille, ajustez de gauche à droite, de bas en haut, et ajustez l'arbre binaire complet à une grande pile de racines

a. Trouvez le premier nœud non feuille 6, car le nœud enfant droit de 6 est supérieur à 6, échangez donc 6 et 9. Après l'échange, il se conforme à la structure du gros tas de racines.

image

c. Trouvez le deuxième nœud non feuille 4, puisque le nœud enfant gauche de 4 est supérieur à 4, échangez donc 4 et 9. Une fois que l'échange n'est pas conforme à la structure du gros tas de racines, continuez à ajuster de droite à gauche et de bas en haut.

image

image

  • Étape 2: échangez les éléments du tas (échangez les éléments de tête et de queue pour obtenir le plus grand élément)

image

  • Étape 3: Reconstruisez le gros tas de racines (les n-1 premiers éléments)

image

  • Répétez les étapes 2 et 3 jusqu'à ce que toute la séquence soit dans l'ordre

image

Source de l'image: https://www.cnblogs.com/chengxiao/p/6129630.html

Exemple de code:

#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. Résumé

  • La complexité temporelle du tri du tas est principalement composée de deux parties: le processus d'initialisation du tas et de reconstruction du tas après avoir fait sauter l'élément supérieur à chaque fois
  • La complexité temporelle du processus d'initialisation de la construction d'un tas est O (n): En supposant que la hauteur du tas est k, à partir du nœud à droite de l'avant-dernière couche, les nœuds de cette couche doivent comparer les nœuds enfants et choisir d'échanger, le troisième au dernier Les couches sont similaires, jusqu'à la première couche (c'est-à-dire que le nombre de couches est de k-1 à 1); alors le temps total est (2 (i-1)) * (ki), où i représente la i-ème couche (la plage est k-1) À 1), 2 (i-1) indique le nombre d'éléments sur le calque, (ki) indique le nombre de comparaisons sur le sous-arbre, c'est-à-dire S = 2 ^ (k-2) * 1 + 2 ^ (k-3) 2 + 2 ^ (k-4) 3 +… + 2 ^ 1 (k-2) + 2 ^ 0 (k-1), en utilisant la soustraction échelonnée (utilisez la constante 2 pour faciliter la conversion, multipliez les deux côtés par 2 et soustrayez Supprimez l'équation d'origine) pour obtenir S = 2 ^ (K-1) + 2 ^ (K-2) + 2 ^ (K-3) +… + 2- (K-1), ignorer le dernier terme constant est juste en attente La séquence de rapport, c'est-à-dire S = 2 k-2- (k-1) = 2 kk-1, et comme k est la profondeur d'un arbre binaire complet, donc 2 ^ k <= n <2 ^ k-1, nous pouvons considérer k = logn, en résumé, S = n-logn -1, donc la complexité en temps est O (n)
  • La complexité temporelle du processus de reconstruction du tas après avoir éclaté l'élément supérieur du tas est O (nlogn): boucle n-1 fois, à chaque fois du nœud suivant à la recherche en boucle, donc à chaque fois est logn, le temps total est (n-1) * logn = nlogn-logn
  • Par conséquent, la complexité temporelle du tri de tas est O (n) + O (nlogn) = O (nlogn)
  • Le tri de tas est un tri basé sur la masse, donc la complexité de l'espace est constante O (1)

Je suppose que tu aimes

Origine blog.csdn.net/qq_43801020/article/details/108136124
conseillé
Classement