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)