Aller au contenu

Sélection en place

Le tri par sélection écrit en page précédente renvoie une nouvelle liste. On aimerait agir sur la liste initiale, c'est à dire écrire une version du tri par sélection qui ne renvoie rien mais trie la liste initiale.

On s'interdit donc de créer une nouvelle liste, on doit trier en échangeant entre eux des éléments de la liste initiale (mais en respectant l'idée du tri par sélection).

Note

Rappelons ici que trier en place une liste sans création d'une nouvelle liste permet un gain d'utilisation de la mémoire. On travaille avec une seule liste plutôt que deux: avec de grandes listes, le gain peut être important. Un gain en temps est également souvent une conséquence de ce choix du tri en place.

Principe

On dispose d'un jeu de cartes et chaque carte est posée dans une "case":

carte carte carte carte carte carte carte

On doit trier le jeu par le principe de sélection du maximum mais on n'a pas le droit de poser les cartes en dehors de la boîte à cases. La seule opération permise est d'échanger deux cartes de place.

Comment pourriez-vous procéder ?

Une réponse
  • On recherche la plus grande carte et on l'échange avec la carte occupant la dernière case:

    carte carte carte carte carte carte carte

    On ne touchera plus à la partie à fond jaune.

  • On recommence: on cherche, dans la partie à fond blanc, la plus grande carte et on l'échange avec la carte occupant la dernière position blanche:

carte carte carte carte carte carte carte

  • On recommence: on cherche, dans la partie à fond blanc, la plus grande carte et on l'échange avec la carte occupant la dernière position blanche:

carte carte carte carte carte carte carte

  • On recommence: on cherche, dans la partie à fond blanc, la plus grande carte et on l'échange avec la carte occupant la dernière position blanche:

carte carte carte carte carte carte carte

  • et ainsi de suite jusqu'à ce que tout le fond soit jaune.

Exercice

Écrire une version de la fonction rechercheIndiceMax suivant la spécification suivante:

def rechercheIndiceMax(tab, debut, fin):
    """
    tab -- liste d'entiers non vide 
    debut -- indice de tab
    fin -- indice de tab

    renvoie l'indice (debut <= indice <= fin) d'un élément 
    de valeur maximale parmi les éléments de tab[debut..fin].
    >>> rechercheIndiceMax([15, 6, 7, 8, 9], 1,3)
    3
    >>> rechercheIndiceMax([15, 6, 7, 8, 9], 0,3)
    0
    """
Solution
def rechercheIndiceMax(tab, debut, fin):
    """
    tab -- liste d'entiers non vide 
    debut -- indice de tab
    fin -- indice de tab

    renvoie l'indice (debut <= indice <= fin) d'un élément 
    de valeur maximale parmi les éléments de tab[debut..fin].
    >>> rechercheIndiceMax([15, 6, 7, 8, 9], 1,3)
    3
    >>> rechercheIndiceMax([15, 6, 7, 8, 9], 0,3)
    0
    """
    m = tab[debut] # sera le max de tab[debut..fin]
    indice = debut # sera l'indice du max de tab[debut..fin]
    for i in range(debut+1, fin+1):
        if tab[i] > m:
            m = tab[i]
            indice = i
    return indice

Exercice

Écrire maintenant une version du tri par sélection du maximum qui trie en place la liste passée en argument.

Principe

On note n le nombre d'éléments de la liste tab.
Le principe est le suivant:

  • rechercher l'indice im du max de tab[0..(n-1)]
  • échanger les valeurs de tab[im] et de tab[n-1].
  • rechercher l'indice im du max de tab[0..(n-2)]
  • échanger les valeurs de tab[im] et de tab[n-2].
  • rechercher l'indice im du max de tab[0..(n-3)].
  • échanger les valeurs de tab[im] et de tab[n-3].
  • etc...

A chaque étape, la queue de liste est triée et contient les valeurs maximales; le début de liste reste à trier.

Solution

Un code possible:

def rechercheIndiceMax(tab, debut, fin):
    """
    tab -- liste d'entiers non vide 
    debut -- indice de tab
    fin -- indice de tab

    renvoie l'indice (debut <= indice <= fin) d'un élément 
    de valeur maximale parmi les éléments de tab[debut..fin].
    >>> rechercheIndiceMax([15, 6, 7, 8, 9], 1,3)
    3
    >>> rechercheIndiceMax([15, 6, 7, 8, 9], 0,3)
    0
    """
    m = tab[debut] # sera le max de tab[debut..fin]
    indice = debut # sera l'indice du max de tab[debut..fin]
    for i in range(debut+1, fin+1):
        if tab[i] > m:
            m = tab[i]
            indice = i
    return indice




def triSelection(tab):
    """
    tab -- liste d'entiers

    Modifie tab en triant ses éléments en ordre croissant.
    """
    for i in range(0, len(tab)-1):
        im = rechercheIndiceMax(tab, 0, len(tab)-1-i)
        tab[len(tab)-1-i], tab[im] = tab[im], tab[len(tab)-1-i]




# TESTS
from random import randint
A = [randint(-10,10) for i in range(randint(1, 10))]
# test de notre tri avec le tri built-in:
B = [x for x in A]
triSelection(A)
B.sort()
print(A == B)

Exercice

Réécrire le tri par sélection en place de l'exercice précédent mais par une sélection du minimum.

Adaptation du principe

Dans la version avec max, on plaçait le maximum en fin de liste et on faisait évoluer l'indice marquant la fin de liste restant à trier.

Dans la version avec min, on place les min en début de liste en faisant évoluer l'indice marquant le début de liste restant à trier.

Solution

Un code possible:

def rechercheIndiceMin(tab, debut, fin):
    """
    tab -- liste d'entiers non vide 
    debut -- indice de tab
    fin -- indice de tab

    renvoie l'indice (debut <= indice <= fin) d'un élément 
    de valeur minimale parmi les éléments de tab[debut..fin].
    >>> rechercheIndiceMin([15, 6, 7, 8, 9], 1,3)
    1
    >>> rechercheIndiceMin([15, 6, 7, 8, 9], 0,3)
    1
    """
    m = tab[debut] # sera le min de tab[debut..fin]
    indice = debut # sera l'indice du min de tab[debut..fin]
    for i in range(debut+1, fin+1):
        if tab[i] < m:
            m = tab[i]
            indice = i
    return indice




def triSelection(tab):
    """
    tab -- liste d'entiers

    Modifie tab en triant ses éléments en ordre croissant.
    """
    for i in range(0, len(tab)-1):
        im = rechercheIndiceMin(tab, i, len(tab)-1)
        tab[i], tab[im] = tab[im], tab[i]




# TESTS
from random import randint
A = [randint(-10,10) for i in range(randint(1, 10))]
# test de notre tri avec le tri built-in:
B = [x for x in A]
triSelection(A)
B.sort()
print(A == B)