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 :

Algorithme de tri de tas

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.

Crรฉer un tri par tas avec un exemple

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.

Crรฉer un tri par tas avec un exemple

ร‰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.

Crรฉer un tri par tas avec un exemple

Crรฉer un tri par tas avec un exemple

ร‰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.

Crรฉer un tri par tas avec un exemple

Crรฉer un tri par tas avec un exemple

ร‰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.

Crรฉer un tri par tas avec un exemple

ร‰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.

Crรฉer un tri par tas avec un exemple

Crรฉer un tri par tas avec un exemple

ร‰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.

Crรฉer un tri par tas avec un exemple

Crรฉer un tri par tas avec un exemple

ร‰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.

Crรฉer un tri par tas avec un exemple

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.

Tas minimum et tas maximum
Tas minimum et tas maximum

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 :

Ajout d'un nouveau nล“ud et Heapify
Ajouter un nouveau nล“ud et tasifier

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 :

Reprรฉsentation basรฉe sur un tableau du tas maximum
Reprรฉsentation basรฉe sur un tableau du tas maximum

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 :

  1. Meilleur cas
  2. Cas moyen
  3. 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.

Analyse de la complexitรฉ temporelle et spatiale

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).

Rรฉsumez cet article avec :