Md-IUT-Cours/algo_avancee/files.md

5.7 KiB

Les files

Le principe est celui des files d'attentes : premier arrivé, premier sorti.

Primitives

Déclaration

f = creer_pile()
bool : file_vide(f)
bool : file_pleine(f)
ajout_fin_file()
elt = debut_file()
retirer_debut_file()

Définition (listes)

Maillon struct (
    valeur : INT
    suivant : SAME)

File struct (
    debut : Maillon
    fin : Maillon)
def creer_file():
    file = File(debut=None, fin=None)
    return file

def file_vide(file):
    return file.debut is None

def file_pleine(file):
    return False

def ajout_fin_file(file, elt):
    mail = Maillon(valeur=elt, suivant=None)
    if file.fin:
        file.fin.suivant = mail
    else:
        file.debut = mail
    file.fin = mail

def debut_file(file):
    return file.debut.valeur

def retirer_debut_file(file):
    mail = file.debut
    file.debut = file.debut.suivant
    if not file.debut:
        file.fin = None
    free(mail)

Les files

Primitives

Définition (tableaux)

La structure de données utilisée sera la suivante :

File struct (
    deb : int
    fin : int
    tab : tableau de X)

Et on implémentera les primitives comme suit :
Les indices du tableau bouclent, de sorte que lorsqu'on atteint la fin théorique du tableau, si on a déjà retiré des éléments au début on peut encore en ajouter, et ceux-ci seront ajoutés au début du tableau.
Ainsi, la file n'est pleine que lorsque l'indice de fin est juste avant l'indice de début, modulo la taille du tableau.

# On considèrera que la constante MAX est définie
# et correspond à la taille maximale possible d'un tableau

def creer_file():
    """
    On crée la file en définissant les indices de début à -1
    et en créant un tableau vide de taille MAX.
    """
    file = File(deb=-1, fin=-1, tab=numpy.empty(MAX, object))
    return file

def file_vide(file):
    """
    La file est vide si les deux indices sont à -1.
    On s'arrangera pour les remettre à -1 si la file devait se vider.
    """
    return file.deb == -1

def file_pleine(file):
    """
    La file est pleine si les indices sont consécutifs
    à la taille du tableau près :
    si en ajoutant 1 à l'indice de fin on obtient l'indice de début,
    la file est pleine.
    """
    return (file.fin + 1) % MAX == file.deb

def ajout_fin_file(file, elt):
    """
    Si la file est vide, on pense à décaler l'indice de début.
    Puis on décale dans tous les cas l'indice de fin
    et on ajoute notre élément.
    """
    if file.deb == -1:
        file.deb += 1
    file.fin += 1
    file.fin %= MAX
    file.tab[file.fin] = elt

def debut_file(file):
    """
    On retourne simplement la valeur contenue dans la case de début.
    """
    return file.tab[file.deb]

def retirer_debut_file(file):
    """
    Si les deux indices sont égaux, ça signifie que la file
    ne contient qu'un seul élément. En en retirant un, la file devient vide.
    On met donc à -1 les indices pour nous permettre de le reconnaître.
    Sinon on incrémente simplement l'indice de début,
    à la taille du tableau près.
    """
    if file.deb == file.fin:
        file.deb = -1
        file.fin = -1
    else:
        file.deb += 1
        file.deb %= MAX

Exercices

Exercice 1

Écrire un algo qui affiche les éléments d'une file en la conservant.

1ère solution

Ici on passe par une autre file : On défile chaque élément pour le mettre dans l'autre file, on affiche chaque élément défilé, puis on change la référence de la file.

def affiche_file(f):
    f_temp = creer_file()

    while !file_vide(f):
        print(debut_file(f))
        ajouter_fin_file(f_temp, debut_file(f))
        retirer_debut_file(f)

    f = f_temp
    return f

2ème solution

Cette solution est dite "en place", parce qu'on n'a pas besoin d'une file supplémentaire. On défile chaque élément pour le rajouter à la fin de la file. On définit préalablement un maillon qui indiquera la fin de la file pour ne pas répéter indéfiniment.

def afficher_file(f):
    """
    On définit une str "***" qui marque la fin de la file.
    La file ne doit pas contenir cet élément.
    """
    fin_file = "***"
    ajouter_fin_file(f, fin_file)

    while (debut_file != fin_file):
        print(debut_file(f))
        ajouter_fin_file(f, debut_file(f))
        retirer_debut_file(f)

    retirer_debut_file(f) # On retire le marqueur de fin.

Exercice 2

Écrire une fonction récursive de recherche dans une file non triée.

Solution

Ici la file est modifiée, tous les éléments précédant l'élément recherché sont supprimés.

def rech_recursive(f, elt):
    if file_vide(f):
        return False
    if debut_file(f) == elt:
        return True
    
    retirer_debut_file(f)
    return(rech_recursive(f, elt))

Exercice 3

Écrire une fonction qui inverse une file.

Solution

def inverse(f):
    p = creer_pile()
    while !file_vide(f):
        empile(p, debut_file(f))
        retirer_debut_file(f)

    while !pile_vide(p):
        ajouter_fin_file(f, sommet(p))
        depile(p)

    return(f)

Exercice 4

Soit une file d'entiers. Écrire un algorithme qui réordonne la file de sorte que les entiers pairs soient en début de file.

Solution

def tri_pairs(f):
    f_pairs = creer_file()
    f_impairs = creer_file()
    
    while !file_vide(f):
        debut = debut_file(f)
        if debut % 2 == 0:
            ajouter_fin_file(f_pairs, debut)
        else:
            ajouter_fin_file(f,_impairs debut)
        
        retirer_debut_file(f)

    while !file_vide(f_impairs):
        ajouter_fin_file(f_pairs, debut_file(f_impairs))
        retirer_fin_file(f_impairs)