Algorithme de tri par tas (avec code dans Python et C++)
Quโest-ce que lโalgorithme de tri par tas ?
Heap Sort est lโun des algorithmes de tri les plus populaires et les plus rapides. Il est construit sur la structure complรจte des donnรฉes de l'arborescence binaire. Nous rechercherons l'รฉlรฉment maximum et le placerons en haut pour le tas maximum. Nous allons le mettre sur le nลud parent de l'arbre binaire.
Disons qu'un tableau est donnรฉ, donnรฉes = [10,5, 7, 9, 4, 11, 45, 17, 60].
Dans le tableau, si le i-iรจme (i=0,1,2,3 โฆ) index est un nลud parent alors, (2i+1) et (2i+2) seront les enfants gauche et droit. La crรฉation d'un arbre binaire complet avec ce tableau ressemblera ร ceci :
Nous effectuerons le processus heapify du dรฉbut ร la fin du tableau. Initialement, si nous convertissons le tableau en arbre, il ressemblera ร celui ci-dessus. Nous pouvons voir qu'il ne conserve aucune propriรฉtรฉ de tas (min-heap ou max tas). Nous obtiendrons le tableau triรฉ en effectuant le processus heapify pour tous les nลuds.
Application du tri par tas
Voici une utilisation de l'algorithme de tri par tas :
- La construction de ยซ files dโattente prioritaires ยป nรฉcessite un tri par tas. Parce que le tri par tas maintient l'รฉlรฉment triรฉ aprรจs chaque insertion.
- La structure de donnรฉes du tas est efficace pour trouver le kth le plus grand รฉlรฉment d'un tableau donnรฉ.
- Le noyau Linux utilise le tri par tas par dรฉfaut algorithme de tri car il a une complexitรฉ spatiale O (1).
Crรฉer un tri par tas avec un exemple
Ici, nous allons construire un tas maximum ร partir de l'arbre binaire complet suivant.
Les nลuds feuilles sont 17, 60, 4, 11 et 45. Ils n'ont aucun nลud enfant. C'est pourquoi ce sont des nลuds feuilles. Nous allons donc dรฉmarrer la mรฉthode heapify ร partir de leur nลud parent. Voici les รฉtapes :
รtape 1) Sรฉlectionnez le sous-arbre le plus ร gauche. Si les nลuds enfants sont plus grands, รฉchangez le nลud parent avec le nลud enfant.
Ici, le nลud parent est 9. Et les nลuds enfants sont 17 et 60. Comme 60 est le plus grand, 60 et 9 seront รฉchangรฉs pour maintenir le tas max.
รtape 2) Dรฉsormais, le sous-arbre le plus ร gauche est regroupรฉ. Le nลud parent suivant est 7. Ce parent a deux nลuds enfants et le plus grand est 45. Ainsi, 45 et 7 seront รฉchangรฉs.
รtape 3) Les nลuds 60 et 4 ont le nลud parent 5. Comme ยซ 5 ยป est plus petit que le nลud enfant 60, il sera interverti.
รtape 4) Dรฉsormais, le nลud 5 a le nลud enfant 17,9. Cela ne maintient pas la propriรฉtรฉ max tas. Ainsi, 5 sera remplacรฉ par 17.
รtape 5) Le nลud 10 sera remplacรฉ par le nลud 60, puis par le nลud 17. Le processus ressemblera ร ce qui suit.
รtape 6) Jusqu'ร l'รฉtape 5, nous avons crรฉรฉ le tas maximum. Chaque nลud parent est plus grand que ses nลuds enfants. Le nลud racine a la valeur maximale (60).
Remarque : Pour crรฉer le tableau triรฉ, nous devons remplacer le nลud de valeur maximale par son successeur.
Ce processus est appelรฉ "extraire maxยป. Comme 60 est le nลud max, nous allons fixer sa position au 0รจme index et crรฉer le tas sans nลud 60.
รtape 7) Lorsque 60 est supprimรฉ, la valeur maximale suivante est 45. Nous allons refaire le processus ยซ Extraire Max ยป ร partir du nลud 45.
Cette fois, nous obtiendrons 45 et remplacerons le nลud racine par son successeur 17.
Nous devons performer ยปExtraire maximumยป jusqu'ร ce que tous les รฉlรฉments soient triรฉs.
Aprรจs avoir effectuรฉ ces รฉtapes jusqu'ร ce que nous extrayions toutes les valeurs maximales, nous obtiendrons le tableau suivant.
Quโest-ce que le tas binaire ?
Un tas binaire est une sorte de tas complet arbre binaire Structure de donnรฉes. Dans ce type de structure arborescente, le nลud parent est soit plus grand, soit plus petit que les nลuds enfants. Si le nลud parent est plus petit, alors le tas est appelรฉ ยซ Min Heap ยป et si le nลud parent est plus grand, le tas est appelรฉ ยซ Max Heap ยป.
Voici des exemples de tas min et max.

Dans la figure ci-dessus, si vous remarquez le ยซ Min Heap ยป, le nลud parent est toujours plus petit que ses nลuds enfants. En tรชte de lโarbre, on retrouve la plus petite valeur 10.
De mรชme, pour le ยซ Max Heap ยป, le nลud parent est toujours plus grand que les nลuds enfants. L'รฉlรฉment maximum est prรฉsent au nลud principal du ยซ Max Heap ยป.
Quโest-ce que ยซ Heapify ยป ?
ยซ Heapify ยป est le principe du tas qui assure la position du nลud. Dans Heapify, un tas maximum maintient toujours une relation avec le parent et l'enfant, et ce nลud parent sera plus grand que les nลuds enfants.
Par exemple, si un nouveau nลud est ajoutรฉ, nous devons remodeler le tas. Cependant, nous devrons peut-รชtre modifier ou รฉchanger les nลuds ou rรฉorganiser le tableau. Ce processus de remodelage d'un tas est appelรฉ ยซ heapify ยป.
Voici un exemple du fonctionnement de heapify :

Voici les รฉtapes pour heapify :
รtape 1) Ajout du nลud 65 comme enfant droit du nลud 60.
รtape 2) Vรฉrifiez si le nลud nouvellement ajoutรฉ est supรฉrieur au parent.
รtape 3) Comme il est supรฉrieur au nลud parent, nous avons รฉchangรฉ le bon enfant avec son parent.
Comment construire le tas
Avant de construire le tas ou de regrouper un arbre, nous devons savoir comment nous allons le stocker. Comme le tas est un arbre binaire complet, il est prรฉfรฉrable d'utiliser un tableau pour contenir les donnรฉes du tas.
Disons qu'un tableau contient un total de n รฉlรฉments. Si le ยซ i ยป รจme index est un nลud parent, alors le nลud de gauche sera ร lโindex (2i+1), et le nลud de droite sera ร l'index (2i+2). Nous supposons que l'index du tableau commence ร 0.
En utilisant cela, stockons un tas maximum dans un tableau semblable ร celui-ci :

L'algorithme heapify conserve la propriรฉtรฉ tas. Si le parent n'a pas la valeur extrรชme (infรฉrieure ou supรฉrieure), il sera รฉchangรฉ avec le nลud enfant le plus extrรชme.
Voici les รฉtapes pour regrouper un tas maximum :
รtape 1) Commencez par le nลud feuille.
รtape 2) Trouvez le maximum entre le parent et les enfants.
รtape 3) รchangez les nลuds si le nลud enfant a une valeur supรฉrieure ร celle du parent.
รtape 4) Montez d'un niveau.
รtape 5) Suivez les รฉtapes 2,3,4 jusqu'ร ce que nous atteignions l'index 0 ou trions l'arbre entier.
Voici le pseudo-code pour heapify rรฉcursif (tas max) :
def heapify(): inputโ array, size, i largest = i left = 2*i + 1 right = 2*i + 2 if left<n and array[largest ] < array[left]: largest = left if right<n and array[largest ] < array[right]: largest = right If largest not equals i: swap(array[i],array[largest]) heapify(array,n,largest)
Pseudo-code pour le tri par tas
Voici le pseudo-code de l'algorithme de tri par tas :
Heapify(numbers as an array, n as integer, i as integer): largest = i left = 2i+1 right= 2i+2 if(left<=n) and (numbers[i]<numbers[left]) largest=left if(right<=n) and (numbers[i]<numbers[right]) largest=right if(largest != i) swap(numbers[i], numbers[largest]) Heapify(numbers,n,largest) HeapSort(numbers as an array): n= numbers.size() for i in range n/2 to 1 Heapify(numbers,n,i) for i in range n to 2 Swap numbers[i] with numbers[1] Heapify(numbers,i,0)
Exemple de code de tri de tas dans C++
#include <iostream>
using namespace std;
void display(int arr[], int n)
{
for (int i = 0; i < n; i++)
{
cout << arr[i] << "\t";
}
cout << endl;
}
void heapify(int numbers[], int n, int i)
{
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < n && numbers[left] < numbers[largest])
{
largest = left;
}
if (right < n && numbers[right] < numbers[largest])
{
largest = right;
}
if (largest != i)
{
//uncomment the following line to see details in output
//cout<<"Swapping "<< numbers[i]<< " and "<<numbers[largest]<<endl;
swap(numbers[i], numbers[largest]);
heapify(numbers, n, largest);
}
}
void heapSort(int numbers[], int n)
{
for (int i = n/2 - 1; i >= 0; i--)
{
heapify(numbers, n, i);
//uncomment the following line to see details in output
//cout<<"Heapify:\t";
//display(numbers,n);
}
for (int i = n - 1; i >= 0; i--)
{
swap(numbers[0], numbers[i]);
heapify(numbers, i, 0);
}
}
int main()
{
int numbers[] = { 10,5, 7, 9, 4, 11, 45, 17, 60};
int size = sizeof(numbers) / sizeof(numbers[0]);
cout<<"Initial Array:\t";
display(numbers,size);
heapSort(numbers, size);
cout<<"Sorted Array (descending order):\t";
display(numbers, size);
}
Sortie :
Initial Array: 10 5 7 9 4 11 45 17 60 Sorted Array (descending order): 60 45 17 11 10 9 7 5 4
Exemple de code de tri de tas dans Python
def display(arr):
for i in range(len(arr)):
print(arr[i], end = "\t")
print()
def heapify(numbers, n, i):
largest = i
left = 2 * i + 1
right = 2 * i + 2
if left < n and numbers[left] < numbers[largest]:
largest = left
if right < n and numbers[right] < numbers[largest]:
largest = right
if largest != i:
numbers[i], numbers[largest] = numbers[largest], numbers[i]
heapify(numbers, n, largest)
def heapSort(items, n):
for i in range(n //2,-1,-1):
heapify(items, n, i) for i in range(n - 1, -1, -1):
items[0], items[i] = items[i], items[0] heapify(items, i, 0) numbers = [10, 5, 7, 9, 4, 11, 45, 17, 60] print("Initial List:\t", end = "") display(numbers) print("After HeapSort:\t", end = "") heapSort(numbers, len(numbers)) display(numbers)
Sortie :
Initial List: 10 5 7 9 4 11 45 17 60 After HeapSort: 60 45 17 11 10 9 7 5 4
Analyse de la complexitรฉ temporelle et spatiale du tri par tas
Il existe une complexitรฉ temporelle et une complexitรฉ spatiale que nous pouvons analyser pour le tri par tas. Pour la complexitรฉ temporelle, nous avons les cas suivants :
- Meilleur cas
- Cas moyen
- Pire cas
Le tas est implรฉmentรฉ sur un arbre binaire complet. Ainsi, au niveau infรฉrieur de lโarbre binaire, il y aura le nombre maximum de nลuds. Si le niveau infรฉrieur a n nลuds, alors le niveau supรฉrieur aura n/2 nลuds.
Dans cet exemple, le niveau 3 comporte quatre รฉlรฉments, le niveau 2 comporte deux รฉlรฉments et le niveau 1 comporte un รฉlรฉment. S'il y a un nombre total de n รฉlรฉments, la hauteur ou le niveau total sera Historique2(n). Ainsi, lโinsertion dโun seul รฉlรฉment peut prendre un maximum de Log(n) itรฉrations.
Lorsque nous voulons prendre la valeur maximale du tas, nous prenons simplement le nลud racine. Lร encore, exรฉcutez le heapify. Chaque tas prend Historique2(n) temps. Lโextraction du maximum prend un temps O(1).
Meilleur cas de complexitรฉ temporelle pour l'algorithme de tri par tas
Lorsque tous les รฉlรฉments sont dรฉjร triรฉs dans le tableau, il faudra O(n) temps pour construire le tas. Parce que si la liste est triรฉe, l'insertion d'un รฉlรฉment prendra le temps constant O(1).
Ainsi, il faudra O(n) temps pour crรฉer un tas maximum ou un tas min dans le meilleur des cas.
Complexitรฉ du temps moyen d'un cas pour l'algorithme de tri par tas
L'insertion d'un รฉlรฉment ou l'extraction d'un maximum coรปte du temps O(log(n)). Ainsi, la complexitรฉ temporelle moyenne des cas pour lโalgorithme de tri par tas est O(n log(n)).
Dans le pire des cas, complexitรฉ temporelle pour l'algorithme de tri par tas
Semblable au cas moyen, dans le pire des cas, nous pourrions effectuer n fois un tas. Chaque heapify coรปtera du temps O(log(n)). Ainsi, la complexitรฉ temporelle dans le pire des cas sera O(n log(n)).
Complexitรฉ spatiale pour l'algorithme de tri par tas
Le tri par tas est un algorithme conรงu sur place. Cela signifie qu'aucune mรฉmoire supplรฉmentaire ou temporaire n'est nรฉcessaire pour effectuer la tรขche. Si nous voyons l'implรฉmentation, nous remarquerons que nous avons utilisรฉ swap() pour effectuer l'รฉchange des nลuds. Aucune autre liste ou tableau n'รฉtait nรฉcessaire. Ainsi, la complexitรฉ spatiale est O(1).














