Le but est de gérer un ensemble d'intervalles pour l'ensemble des entiers (suivant type/implémentation, signé ou non) semi-ouverts ([X;Y[ avec X < Y) par une liste. Toutefois, il est aisé de passer d'un intervalle ouvert à semi-ouvert, en ajoutant un à la borne supérieure.
Dans le cas d'un recouvrement, une fusion doit être opérée lors de l'insertion pour ne conserver que l'intervalle résultant (le plus grand). Exemple: si l'on possède [1;4[ et que l'on veut insérer [2;7[, on doit aboutir à [1;7[.
Contraintes :
Effets :
Exemples d'application :
L'entrée sélectionnée est écrite dans le même ordre qu'elle est lue (3,1 aboutit en 1,3), et est écrite exactement une fois (3,1-5 aboutit en 1-5)
Détails des cas (+ illustrations)
Intervalle: <borneInférieure: entier, borneSupérieure: entier>
Prototype de la fonction à implémenter :
fonction insérerIntervalle(listeIntervalles: liste[Intervalle], borneInférieure: entier, borneSupérieure: entier)
Si la liste d'intervalles est vide, ajouter (peu importe que ce soit en tête ou en queue) l'intervalle [borneInférieure;borneSupérieure[ :
fonction insérerIntervalle(listeIntervalles: liste[Intervalle], borneInférieure: entier, borneSupérieure: entier)
si estVide(listeIntervalles) alors
ajouterEnQueue(listeIntervalles, créerIntervalle(borneInférieure, borneSupérieure)
sinon
// partie complétée par la suite
fsi
fin
Il existe deux cas différents : cette borne est, ou non (en dehors), située dans un intervalle existant.
borneInférieure dedans :
X1---
borneInférieure dehors (deux sous-cas) :
X2---
X3---
Intervalles de comparaison (L1 = courant, L2 = prochain) :
L1 ---- U1 L2 ---- U2
Cas :
Ne perdons pas de vue que la liste est triée selon la borne inférieure. Par conséquent, si celle-ci est inférieure à celle de l'intervalle courant lors de notre parcours de la liste, il n'est pas utile d'aller plus loin.
ElementDeListe from <- tête(listeIntervalles)
tant que non finListe(listeIntervalles, from) faire
Intervalle i <- valeur(listeIntervalles, from)
si borneInférieure < i.borneInférieure alors // cas X2
si borneSupérieure < i.borneInférieure alors // cas X2_1
ajouterAvant(listeIntervalles, from, créerIntervalle(borneInférieure, borneSupérieure)
retourner
sinon // cas X2_2
fin de la boucle
fsi
sinon // borneInférieure >= i.borneInférieure
si borneInférieure <= i.borneSupérieure // cas X1
si borneSupérieure <= i.borneSupérieure // cas X1_1
retourner // [borneInférieure;borneSupérieure[ compris dans i: fin de la fonction, rien à faire
sinon // cas X1_2
fin de la boucle
fsi
fsi
fsi
from <- successeur(listeIntervalles, from)
ftant
Il ne s'agit là que d'un premier repérage : nous n'avons pas, pour le moment, considéré les intervalles environnants pour déterminer si nous devons procéder à une(des) fusion(s).
Toutefois, on remarquera un nouveau cas particulier à l'issue de cette itération : si from marque la fin de la liste alors l'intervalle est nouveau et disjoint des autres (= pas de chevauchement) : on peut l'ajouter en fin de liste et mettre fin à la fonction.
si finListe(listeIntervalles, from) alors
ajouterEnQueue(listeIntervalles, créerIntervalle(borneInférieure, borneSupérieure)
retourner // inutile de poursuivre
fsi
borneInférieure dedans :
---Y1
borneInférieure dehors (deux sous-cas) :
---Y2
---Y3
Intervalle de comparaison :
L ---- U
Ayant déjà assuré, lors du repérage du placement de la borne inférieure, que l'intervalle que nous cherchons à ajouter est contenu dans un autre, l'intervalle représenté sur la figure par [L;U[ ne peut être que différent de celui désigné par from (représenté par [L1;U1[ dans la figure de la partie précédente).
Cas :
ElementDeListe prev <- from
ElementDeListe to <- successeur(listeIntervalles, from)
tant que non finListe(listeIntervalles, to) faire
Intervalle i <- valeur(listeIntervalles, to)
si i.borneInférieure >= borneSupérieure // cas Y2
to <- prev
fin de la boucle
fsi
si i.borneSupérieure >= borneSupérieure // cas Y1 (le si ci-dessus implique i.borneInférieure < borneSupérieure)
fin de la boucle
fsi
prev <- to
to <- successeur(listeIntervalles, to)
ftant
Si à l'issue de l'itération, la fin de la liste est atteinte, nous réassignons à to la queue de la liste (prev) :
si finListe(listeIntervalles, to) alors
to <- prev
fsi
X1 ----------------------- Y1
X2 ------------------ Y2
X3 ---------- Y3
X4 ----------------- Y4
X5 ------------------------------------ Y5
L1 ---- U1 L2 ---- U2 L3 ---- U3 L4 ---- U4
Etapes :
ElementDeListe el <- successeur(listeIntervalles, from)
tant que el != successeur(listeIntervalles, to) faire // jusqu'à to compris
el <- supprimerElement(listeIntervalles, el)
ftant
Intervalle i <- valeur(listeIntervalles, from)
i.borneInférieure = minimum(i.borneInférieure, borneInférieure)
i.borneSupérieure = maximum(i.borneSupérieure, borneSupérieure)
fonction insérerIntervalle(listeIntervalles: liste[Intervalle], borneInférieure: entier, borneSupérieure: entier)
si estVide(listeIntervalles) alors
ajouterEnQueue(listeIntervalles, créerIntervalle(borneInférieure, borneSupérieure)
sinon
ElementDeListe from <- tête(listeIntervalles)
tant que non finListe(listeIntervalles, from) faire
Intervalle i <- valeur(listeIntervalles, from)
si borneInférieure < i.borneInférieure alors // cas X2
si borneSupérieure < i.borneInférieure alors // cas X2_1
ajouterAvant(listeIntervalles, from, créerIntervalle(borneInférieure, borneSupérieure)
retourner
sinon // cas X2_2
fin de la boucle
fsi
sinon // borneInférieure >= i.borneInférieure
si borneInférieure <= i.borneSupérieure // cas X1
si borneSupérieure <= i.borneSupérieure // cas X1_1
retourner // [borneInférieure;borneSupérieure[ compris dans i: fin de la fonction, rien à faire
sinon // cas X1_2
fin de la boucle
fsi
fsi
fsi
from <- successeur(listeIntervalles, from)
ftant
si finListe(listeIntervalles, from) alors
ajouterEnQueue(listeIntervalles, créerIntervalle(borneInférieure, borneSupérieure)
retourner // inutile de poursuivre
sinon
// partie fusion
ElementDeListe prev <- from
ElementDeListe to <- successeur(listeIntervalles, from)
tant que non finListe(listeIntervalles, to) faire
Intervalle i <- valeur(listeIntervalles, to)
si i.borneInférieure >= borneSupérieure // cas Y2
to <- prev
fin de la boucle
fsi
si i.borneSupérieure >= borneSupérieure // cas Y1 (le si ci-dessus implique i.borneInférieure < borneSupérieure)
fin de la boucle
fsi
prev <- to
to <- successeur(listeIntervalles, to)
ftant
si finListe(listeIntervalles, to) alors
to <- prev
fsi
ElementDeListe el <- successeur(listeIntervalles, from)
tant que el != successeur(listeIntervalles, to) faire // jusqu'à to compris
el <- supprimerElement(listeIntervalles, el)
ftant
Intervalle i <- valeur(listeIntervalles, from)
i.borneInférieure = minimum(i.borneInférieure, borneInférieure)
i.borneSupérieure = maximum(i.borneSupérieure, borneSupérieure)
fsi
fsi
fin
J'ai opté pour une variante de liste doublement chaînée où cette liste est en fait accédée par un objet intermédiaire de plus haut niveau. Ce, pour plusieurs raisons :
Source C intégrée à mon projet ugrep (avec parsing des intervalles)