Plan
Langage Java
• Exceptions Algorithmique
• Implantations d'un graphe
• Parcours de graphe
Exceptions
Une exception est un objet de la classe java.lang.Exception (ou de l’une de ses sous-classes)
class ExceptionPile extends Exception { ExceptionPile(String m)
{ System.out.println(m) } }
Utilisation :
int valeur() throws ExceptionPile { if (estVide())
throw new ExceptionPile("Pile vide");
return contenu[hauteur-1];
}
Exceptions
Effets de la levée d'exception, par
"throws"
(1) création d'un objet de la classe ExceptionPile
(2) sortie de la méthode en cours (3) recherche dans l'arbre d'appel d'un bloc qui capte l'exception
Pile p = new Pile();
try // Exécution contrôlée { System.out.println(p.valeur());
}catch(ExceptionPile e) { System.out.println(m) } ...
Exceptions
Le bloc try lance l'exécution.
Si une erreur se produit pendant cette exécution, l'éxécution se
poursuit dans un bloc catch, avec comme argument e l'objet créé lors de la levée d'exception.
Pile p = new Pile();
try // Exécution contrôlée { System.out.println(p.valeur());
}catch(ExceptionPile e) { System.out.println(m) } ...
Exceptions
On peut avoir 0, 1 ou plusieurs blocs catch, qui se comportent comme un case dans un bloc switch.
try // Exécution contrôlée { readFromFile("monFichier");
}catch(FilelNotFoundException e)
{ System.out.println("Pas trouvé !") }catch(IOException e)
{ System.out.println("Erreur d'entrée- sortie");
}catch(Exception e) // Autres erreurs { System.out.println("Erreur");
}
Finally
On peut rajouter un bloc finally, qui est exécuté que l'exception
soit levée ou pas, même en cas de return, avant que le programme ne quitte le try
try // Exécution contrôlée { readFromFile("monFichier");
}catch { ... }
...finally // nettoyage
{ Refermer les fichiers, les connections Internet, etc.
}
Matrice d'adjacence
Mi,j = {
0 1 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 si (i, j) G
1 si (i, j) G
1 2
3 4
class GrapheMat
{ int[][] m; // Matrice d'adjacence int n; // nombre de sommets GrapheMat(int n) {
this.n = n;
m = new int[n][n];
}
Exemple d'utilisation
Thm Soit M la matrice d'adjacence d'un graphe G. Pour tout n ≥ 0, Mi,j est égal au nombre de chemins de longueur n de i à j.
n
n true s'il existe un chemin de longueur n de i à j false sinon
class GrapheMat {
boolean[][] m; // Matrice d'adjacence int n; // nombre de sommets }
Remarque Si M est définie comme matrice booléenne, on a
Mi,j = {
Matrice d'incidence
1 si i est l'origine de a Mi,a = { -1 si i est l'extrémité de a
0 sinon
Exercice : montrer que M est unimodulaire, i. e. le déterminant de toute sous-matrice carrée de M vaut 0, -1 ou 1.
(Graphe sans boucle)
1 0 1 0 -1 -1 1 0 0 0 0 0 0 -1 1 0 -1 -1 1 0
1 2
3 4
1
2 3
4 5
1 2 3 4 5 1
2 3 4
Par récurrence sur la taille n de la matrice carrée extraite.
• Clair pour n = 1
• On développe le calcul du
déterminant Dn par rapport à une colonne.
- Si une colonne est nulle, Dn = 0 - Si une colonne contient un seul
coefficient non nul, 1 ou -1, Dn = ± Dn-1
- Si toutes les colonnes ont deux coefficients non nuls (1 et -1), la somme des colonnes est nulle et Dn = 0
Solution
Liste de successeurs 1 : 2 4 2 : 4 3 : 3 4 : 3
public class GrapheListe { Liste[] succ;
int n;
GrapheListe(int n) { this.n = n;
succ = new Liste[n];
} }
1 2
3 4
Comparaison des tailles
Graphe à n sommets et m arcs.
Matrice d'adjacence : n2 Matrice d'incidence : nm
Liste de successeurs : n + m
Parcours d'un graphe connexe non orienté à partir d'un sommet s C'est une suite S de sommets t.q.
(1) s est le premier sommet de S (2) Chaque sommet apparaît une
fois et une seule dans S
(3) Tout sommet sauf la racine est adjacent à un sommet placé avant lui dans la liste.
Exemple : 5 3 6 2 1 4 7 est un parcours issu de 5
1
2
3
4 5 6
7
On obtient une arborescence de Trémeaux.
Ici, en partant de s = 1 :
Complexité (n sommets, m arêtes) O(n + m) avec des listes de
successeurs, O(n2) avec des matrices d'incidence
Parcours en profondeur Initialisation : S = (s)
Si S = (s1 , …, sn-1), on prend pour sn un voisin de sn-1, ou, à défaut, un voisin de sn-2, ou, à défaut, un voisin de sn-3, etc.
1
2
3
5 6 7
4
public class Graphe {
Liste[] succ;
boolean[] marque;
int n;
Graphe(int n) {
this.n = n;
succ = new Liste[n];
marque = new boolean[n];
}
void marquer(int i) { marque[i] = true;
}
void demarquer()
{ for (int i = 0; i < n; i++) marque[i] = false;
} } ...
Trémeaux Récursif
void profondeur(int i) {
System.out.print(i + " ");
marquer(i);
for (Liste a = succ[i]; a != null; a = a.suivant)
{
int v = a.contenu;
if (!marque[v]) profondeur(v);
} }
void parcoursProfondeur() {
demarquer();
for (int i = 0; i < n; i++) if (!marque[i])
profondeur(i);
}
Tri topologique
Un graphe est dit sans circuit si sa fermeture transitive est une relation d'ordre partielle sur l'ensemble des sommets.
Prop. Toute relation d'ordre partielle admet une extension linéaire (relation d'ordre total).
Effectuer un tri topologique sur un graphe sans circuit consiste à trouver une extension linéaire.
1
2
3
4 5 6
7
1 < 2 < 3 <4 < 5 < 6 < 7
Tri topologique
On peut le réaliser à partir du parcours (récursif) en profondeur en partant d'un sommet sur lequel aucun arc n'aboutit dans le graphe dual, en affichant les sommets après leur utilisation.
Il suffira d'ajouter un
System.out.print(v + " ")
à la fin de la méthode Tremeaux .
Preuve. S'il existe un arc de i vers j dans le graphe, il existe un arc de j vers i dans le graphe dual.
Nécessairement, i est affiché avant j.
1 < 2, 1 < 3, 2 < 3, 2 < 4, 2 < 6, 3 < 5, 3 < 7, 4 < 7, 5 < 6
3
2 4
5 6
1
2
3
4 5
6
7
Graphe dual
On part de 7 : 1 2 3 4 7 (Ordre postfixe) On repart de 6 : 5 6
Ordre total : 1 < 2 < 3 < 4 < 7 < 5 < 6
2 1
4 5
6
7
Parcours en largeur à partir d'un sommet s F0 = {s}
Fn+1 est constitué de l'ensemble des successeurs des éléments de Fn moins les éléments de
∪
Fk0 ≤ k ≤ n
F = {1}
F = {2, 3}
F = {4, 5, 6, 7}
0
1
2
1
2
3
4 5 6
7
Parcours en largeur
• Réalisé en gérant une file : les sommets «en cours » sont les sommets déjà visités mais dont les successeurs n’ont pas tous été visités.
• largeur(s)
- Marquer s et le mettre dans la file - Tant que la file n’est pas vide,
retirer le premier élément t de la
file, et, pour chaque successeur non marqué de t, le marquer, l ’afficher et le mettre dans la file.
void parcoursLargeur() { demarquer();
for (int i = 0; i < n; i++) if (!marque[i])
largeur(i);
}
void largeur(int i)
{ File F = new File();
marquer(i);
File.ajouter(i, F);
while (!File.EstVide(F)) { int v = File.valeur(F);
System.out.print(v + " ");
File.supprimer(F);
for (Liste a = succ[j]; a != null; a = a.suivant) { int v = a.contenu;
if (!marque[v]) { marquer(v);
File.ajouter(v, F);
} } } }
Arborescences
On a vu comment représenter une arborescence à l'aide de la fonction père (chaque racine est son propre père)
Exemple : [1, 1, 1, 2, 3, 2, 4]
Exercice : Calculer la fonction père en partant de l'une des trois représentations d'un graphe.
Une arborescence maximale Une arborescence maximale
1
2
3
4 5 6
7 1
2
3
4 5 6
7
Arborescence
des plus courts chemins
Soit F un parcours en largeur à partir de s d'un graphe G. Pour chaque sommet v ≠ s, il existe un premier élément v' de F tel que (v', v) soit un arc du graphe. Soit G(s) le sous-graphe constitué de ces arcs.
Proposition
(1) G(s) est une arborescence (2) x ∈ G(s) ssi il existe un
chemin de s à x
(3) Si x ∈ G(s), le chemin de s à x dans G(s) est un plus court chemin de s à x dans G.
Arborescences préfixes
Définition : Pour tout sommet i, l'ensemble des descendants de i est de la forme [i,i'].
1
8 2
9 10 3 5
11 13 12 4 6 7
[1…13]
[8…13] [2…7]
[10…13] [3, 4] [5…7]
9
11 13 12 4 6 7
Problèmes de connections
On veut tester si deux sommets sont dans la même composante connexe.
Si ce n'est pas le cas, on veut
pouvoir rajouter facilement l'arête manquante.
--> Algorithme "Union-Find"
2 1
6
3
4
7 5
8 9
10 11
12 13
1 2
3
4 6 5
7
8 9
10
11
12 13
14
15 16
17
int SontConnectes(int x, int y, boolean AConnecter, Arbo pere)
{ int i = x, j = y;
while (pere[i] > 0) i = pere[i];
while (pere[j] > 0) j = pere[j];
if (AConnecter && (i != j)) pere[j] = i;
return(i == j);
}
Arêtes : (1,7), (1,2), (1,3), (12,13), (10,13), (10,12), (10,11), (4,7), (4,6), (8,9), (5,6), (1,6), (5,7), (3,7), (8,7), (10,7), (12,7)
Pere[1] = 4 Pere[2] = 1 Pere[3] = 1 Pere[4] = 5 Pere[5] = 8 Pere[6] = 4 Pere[7] = 1 Pere[8] = 10 Pere[9] = 8 Pere[10] = 0 Pere[11] = 10 Pere[12] = 10 Pere[13] = 12
Améliorations
Dans le pire des cas, on obtient un arbre filiforme… Pour l'éviter :
(1) équilibrer l'arbre : on mémorise la taille des sous-arbres issus de chaque nœud. Lorsqu'on rajoute une arête (i, j) on prend comme racine celui des deux sommets qui a le plus grand sous- arbre.
(2) comprimer les chemins : on rattache toutes les feuilles directement à la racine!