C'est un tri d'insertion correct mais étrange et inefficace.
Visualisons-le d'abord en imprimant A
après chaque itération complète de la boucle interne. Exemple :
before: [1, 12, 13, 8, 15, 18, 19, 16, 7, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[19, 1, 12, 8, 13, 15, 18, 16, 7, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 19, 12, 8, 13, 15, 18, 16, 7, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 12, 19, 8, 13, 15, 18, 16, 7, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 8, 12, 19, 13, 15, 18, 16, 7, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 8, 12, 13, 19, 15, 18, 16, 7, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 8, 12, 13, 15, 19, 18, 16, 7, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 8, 12, 13, 15, 18, 19, 16, 7, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 8, 12, 13, 15, 16, 18, 19, 7, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 7, 8, 12, 13, 15, 16, 18, 19, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 7, 8, 11, 12, 13, 15, 16, 18, 19, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 6, 7, 8, 11, 12, 13, 15, 16, 18, 19, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 6, 7, 8, 11, 12, 13, 14, 15, 16, 18, 19, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 3, 6, 7, 8, 11, 12, 13, 14, 15, 16, 18, 19, 2, 9, 5, 4, 0, 10, 17]
[1, 2, 3, 6, 7, 8, 11, 12, 13, 14, 15, 16, 18, 19, 9, 5, 4, 0, 10, 17]
[1, 2, 3, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 19, 5, 4, 0, 10, 17]
[1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 19, 4, 0, 10, 17]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 19, 0, 10, 17]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 19, 10, 17]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 17]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
after: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Graphiquement, mais mis à jour après chaque échange, la barre rouge indique l'indice i
( code ici ) :
Pour i = 0
en fait, il hace brouille plus ou moins la liste, mais il apporte la plus grande valeur à A[0]
(ou un la plus grande valeur, s'il y en a plusieurs). À partir de ce moment, cette valeur servira de sentinelle.
En raison de ce désordre initial, il serait difficile (et inutile) d'énoncer un invariant basé sur l'état initial du tableau. Au lieu de cela, définissons A0
pour être l'état du tableau après la boucle externe de i = 0
:
Invariant : Après la boucle externe pour certains i
:
- La valeur globale la plus importante se situe à
A[i]
-
A[0 to i]
contiennent A0[0 to i]
dans un ordre trié.
Preuve par induction :
Cas de base i = 0
est trivial.
Cas i > 0
: Avant la boucle extérieure avec ceci i
nous avons la sentinelle (la plus grande valeur globale) à A[i-1]
y A[0 to i-1]
contient A0[0 to i-1]
dans l'ordre trié. Maintenant la boucle interne passe sur tous les éléments et nous échangeons A[i]
y A[j]
chaque fois que A[j] > A[i]
. Examinons à nouveau un exemple de ligne ci-dessus :
[1, 7, 8, 12, 13, 15, 16, 18, 19, 11, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
El 19
est la sentinelle, la partie jusqu'à elle est triée, et i
est l'indice du 11. Que se passe-t-il lorsque nous utilisons j
de 0 à la fin ? Les valeurs 1, 7 et 8 ne sont pas plus grandes que le 11, donc rien ne se passe. Le 12 est plus grand, donc il sera échangé avec le 11 :
[1, 7, 8, 11, 13, 15, 16, 18, 19, 12, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
Maintenant, c'est le 12 qui est à l'index. i
. Ensuite, il est comparé à 13. Comme 13 est plus grand, ils sont échangés :
[1, 7, 8, 11, 12, 15, 16, 18, 19, 13, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
Cela continue, toujours en échangeant, jusqu'à ce que nous échangions avec la sentinelle :
[1, 7, 8, 11, 12, 13, 16, 18, 19, 15, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 7, 8, 11, 12, 13, 15, 18, 19, 16, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 7, 8, 11, 12, 13, 15, 16, 19, 18, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
[1, 7, 8, 11, 12, 13, 15, 16, 18, 19, 6, 14, 3, 2, 9, 5, 4, 0, 10, 17]
A ce stade, l'insertion du 11 dans la partie triée est terminée. Et la sentinelle s'est déplacée vers l'index i
où il empêche ensuite tout autre échange pendant la boucle interne (c'est-à-dire tout échange avec des éléments situés plus à droite). En général, pour une nouvelle valeur (comme le 11), les nombres plus petits que lui restent où ils sont, et les nombres plus grands que lui sont échangés et replacés à un endroit vers la droite.
Quand nous aurons terminé nous avons eu la boucle extérieure avec i = n-1
. L'invariant nous dit alors que A[0 to n-1]
contiennent A0[0 to n-1]
dans l'ordre trié. C'est-à-dire que le tableau finit par être correctement trié.
Pourquoi je l'appelle une sorte d'insertion : Après le désordre i = 0
Si vous regardez le tableau après chaque boucle interne complète, il est impossible de le distinguer du tri par insertion (voir mon gros bloc de visualisation en haut). Par exemple, le 11 a simplement été inséré dans la partie triée à sa gauche. Ce qui diffère, c'est la façon dont l'insertion se fait. Le tri par insertion normal fait "bouillonner" le 11 vers la gauche jusqu'à sa place correcte, sans même regarder les nombres encore plus petits. Cet algorithme recherche plutôt le point d'insertion, en commençant tout à gauche, puis insère le 11 à cet endroit et fait "bouillonner" les plus grands nombres jusqu'à la sentinelle. vers la droite . Et il continue ensuite avec d'autres comparaisons infructueuses contre la sentinelle qui est maintenant assise à A[i]
.
Mise à jour : smusamashah a partagé leur fantastique outil de visualisation ce qui nous permet de comparer cet algorithme avec les autres algorithmes dont il se réclame. Cochez les cases à droite pour Bubble Sort, Insertion Sort et Selection Sort, puis cliquez sur Start. Vous verrez à nouveau que notre tri ressemble beaucoup au tri par insertion, et pas du tout aux autres. Et si vous faites plutôt Tri d'échange (malheureusement non inclus), vous verrez que cela ressemble plus à un tri par sélection (mais plus lent car il y a plus de permutations et l'outil montre toutes les permutations).