• Aucun résultat trouvé

Rappel : une définition récursive des arbres

N/A
N/A
Protected

Academic year: 2022

Partager "Rappel : une définition récursive des arbres"

Copied!
14
0
0

Texte intégral

(1)

Amphi 5 1

Plan

• Arbres et forêts

• Algorithme union-find

• Ordres sur les mots, parcours d'arbres binaires

• Arbres binaires de recherche

Amphi 5 2

Plan

• Arbres et forêts

• Algorithme union-find

• Ordres sur les mots, parcours d'arbres binaires

• Arbres binaires de recherche

Rappel : une définition récursive des arbres

Un arbre est un ensemble fini de nœuds, tel que : (1) Il existe un nœud particulier appelé racine, (2) Les nœuds restants sont partitionnés en ensembles qui sont eux mêmes des arbres.

5 7 8

4 2

1

3

6 9

T =

(

1,

{

(2, {(5), (6)}), (3, {(7), (8), (9)}), (4)

} )

Forêts

Une forêt est un ensemble fini d'arbres

4 11 10

7 3

14

9

13

12 6 0

8

2 5

1

(2)

Amphi 5 5

Implantation des forêts par des tableaux

On représente la forêt par un tableau pere tel que pere[s] = père de s (si s est une racine, pere[s] = s)

0 1 2 3 4 5 6 7 8 9

4 11 10

7 3

14

9

13

12 6 0

8

2 5

1

pere 2 8 5 14 3 5 2 14 8 14 9 9 3 9 14

1011121314

Amphi 5 6

Plan

• Arbres et forêts

• Algorithme union-find

• Ordres sur les mots, parcours d'arbres binaires

• Arbres binaires de recherche

Partitions

Données : une partition de l'ensemble {0, 1, ..., n-1}

• Trouver la classe d'un élément (find).

• Faire l'union de deux classes (union).

2, 5, 8 0, 6 1 3, 4, 7, 9

0 1 2 3

Première solution : tableau

On représente la partition par un tableau classe tel que classe[i] soit la classe de l'élément i.

P =

{

{2, 5, 8}, {0, 6}, {1}, {3, 4, 7, 9}

}

classe 0 1 2 3

1 2 0 3 3 0 1 3 0 3

Trouver : O(1) Union : O(n)

0 1 2 3 4 5 6 7 8 9 classe

(3)

Amphi 5 9

Deuxième solution : forêts

P =

{

{2, 5, 8}, {0, 6}, {1}, {3, 4, 7, 9}

}

0 1 5 4 7 5 0 7 2 7

0

8 2 5

9 4

7

3 6

1

On représente la forêt par un tableau pere tel que pere[s] = père de s (si s est une racine, pere[s] = s)

Trouver : O(h) Union : O(h) h = hauteur

0 1 2 3 4 5 6 7 8 9 pere

Amphi 5 10

Union

Union des classes de 11 et de 2

4 11 10

7 3

14

9

13

12 6 0

8

2 5

1

Union

Union des classes de 11 et de 2

4 11 10

7 3

14

9

13

12 6 0

8

2 5

1

Union

Union des classes de 11 et de 2

4 11 10

7 3

14

9

13

12 6 0

8

2 5

1

(4)

Amphi 5 13

Union

Union des classes de 11 et de 2

4 11 10

7 3

14

9

13

12 6 0

8

2 5

1

Amphi 5 14

Union

Union des classes de 11 et de 2

4 11 10

7 3

14

9

13 12

6 0

2 5

8

1

Trouver

class UnionFind {

static final int n = 16;

static int[] pere = new int[n];

static int trouver(int x) {

while (pere[x] != x) x = pere[x];

return x;

} }

Union

static void union(int x, int y) {

int r = trouver(x);

int s = trouver(y);

if (r != s) pere[r] = s;

}

(5)

Amphi 5 17

Union pondérée

Règle : Lors de l'union, la racine de l'arbre de moindre taille devient fils de la racine de l'arbre de plus grande taille (la taille est le nombre de nœuds).

6

10

8 4

9 5

1

11 7

3

2

12

13 4

Taille 7 Taille 6

Amphi 5 18

Union pondérée

static int[] taille = new int[n];

// initialisé à 1 (méthode omise)

static void unionPonderee(int x, int y){

int r = trouver(x);

int s = trouver(y);

if (r == s) return;

if (taille[r] > taille[s]) { pere[s] = r;

taille[r] = taille[r] + taille[s];

} else {

pere[r] = s;

taille[s] = taille[r] + taille[s];

} }

Union pondérée

Lemme. La hauteur d'un arbre à n nœuds créé par union pondérée est ! 1 + !log2 n".

Preuve. Par récurrence sur n. Pour n = 1, vrai.

Si un arbre est obtenu par union pondérée d'un arbre à m nœuds et d'un arbre à (n-m) nœuds, avec 1 ! m ! n/2, sa hauteur est majorée par max(1 + !log2(n-m)", 2 + !log2(m)")

Comme log2(m) ! log2(n/2) = log2(n) - 1, et log2(n - m) ! log2(n), la hauteur est majorée par 1 + !log2 n".

n-m m

Trouver, avec compression des chemins

Principe. On remonte du nœud x à sa racine r, puis on refait le parcours en faisant de chaque nœud rencontré un fils de r.

10

8 4

5 9 1

11 7

3

2

10

8

4 9

5

1

11 7

3

2

Trouver 10

(6)

Amphi 5 21

Trouver avec compression des chemins

static int trouverAvecCompression(int x) {

int r = trouver(x);

while (x != r) {

int y = pere[x];

pere[x] = r; // x devient fils de r x = y;

}

return r;

}

Amphi 5 22

Complexité

Théorème (Tarjan) Avec l'union pondérée et la compression des chemins, une suite de n-1

"unions" et de m "trouver" (m " n) se réalise en temps O(n + m.a(n, m)), où a est une fonction quasi-constante.

En fait, a(n,m) ! 2 pour m " n et n < 265536.

Toutefois, Tarjan a montré que l'algorithme précédent n'est pas linéaire !

Définition précise de a

Soit A : N x N --> N la fonction définie par A(i, 0) = 1 pour i " 1, A(0, j) = 2j pour j " 0, A(i+1, j+1) = A(i, A(i+1, j)) pour i, j " 0.

Par exemple, A(2, 5) = 265536 et 2

A(3,4) = 22

Soit a la fonction définie pour m " n par

a(n, m) = min { x " 1 | A(x, #4m/n$) > log n } En fait, a(n, m) ! 2 pour m " n et n < 265536.

}

65536 fois

Deux points d'un réseau sont-ils connectés ?

Les points (1, 3), (2, 3), (5, 4), (6, 3), (7, 5), (1, 6) et (7, 0) sont connectés.

Est-ce que 4 et 6 sont connectés ?

Union-Find résout ce problème efficacement !

0 2

5 4

7 3

6 1

(7)

Amphi 5 25

Equivalence de deux automates déterministes

Amphi 5 26

Equivalence de deux automates déterministes

On calcule (par Union-Find !) la plus petite relation d'équivalence ~ sur Q1 % Q2 telle que les états initiaux soient équivalents (1 ~ 5) et telle que

si p ~ q et si a est une lettre, alors p.a ~ q.a.

On vérifie ensuite que si p ~ q et si p est un état final, alors q est un état final.

Complexité. Si les automates ont n1 et n2 états et l'alphabet a k lettres, algorithme en O(kna(2kn, n)), où n = n1 + n2. Donc quasi-linéaire en kn

Plan

• Arbres et forêts

• Algorithme union-find

• Ordres sur les mots, parcours d'arbres binaires

• Arbres binaires de recherche

Mots

Un alphabet est un ensemble de lettres : {0, 1}, {a, b, c, d, r}

Un mot est une suite de lettres : 01101, abracadabra

La longueur d'un mot u (notée |u|) est le nombre de lettres de u :

|01101| = 5, |abracadabra| = 11

Le mot vide, de longueur 0, est noté e

(8)

Amphi 5 29

Ensembles préfixiels

Concaténation de deux mots :

u = abra, v = cadabra, uv = abracadabra

Un mot p est préfixe (propre) d'un mot u s'il existe un mot v (non vide) tel que u = pv.

e, abr, abrac sont des préfixes propres de abraca.

Un ensemble de mots P est préfixiel si tout préfixe d'un mot de P est lui-même dans P.

{e, 1, 10, 11}, {e, 0, 00, 01, 000, 001} sont des ensembles préfixiels.

Amphi 5 30

Codage d'un arbre binaire

0 1

0

0

0

0 1

1 1

Code : {e, 0, 1, 00, 01, 10, 11, 010, 101, 110}

Un ensemble fini de mots est préfixiel si et seulement si c'est le code d'un arbre binaire.

L'ordre lexicographique

Au départ, on fixe un ordre total sur l'alphabet : 0 < 1, a < b < c < d < r

L'ordre lexicographique (ou du dictionnaire) est défini par u <lex v si et seulement si

- u est un préfixe de v ou

- u = pau' et v = pbv' où p est un mot et a et b sont des lettres telles que a < b.

p p

a b

u' v' u

v u

v

Codage du parcours préfixe

9 10 8

4 5 6 7

2 3

1

Parcours préfixe (CGD) : 1, 2, 4, 8, 9, 5, 10, 3, 6, 7 Codage : e, 0, 00, 000, 001, 01, 010, 1, 10, 11

0 1

0

0 0

0

1 1

1

Le parcours préfixe est codé par l'ordre lexicographique.

(9)

Amphi 5 33

Codage du parcours postfixe

9 10 8

4 5 6 7

2 3

1

Parcours postfixe (GDC) : 8, 9, 4, 10, 5, 2, 6, 7, 3, 1 Codage : 000, 001, 00, 010, 01, 0, 10, 11, 1 , e

0 1

0

0 0

0

1 1

1

Le parcours postfixe est codé par l'ordre opposé de l'ordre lexicographique obtenu en prenant 1 < 0.

Amphi 5 34

Codage du parcours infixe

Infixe (GCD) :

8, 4, 9, 2, 10, 5, 1, 6, 3, 7 000, 00, 001, 0, 010, 01, e, 10, 1, 11

9 10 8

4 5 6 7

2 3

1

0 1

0

0 0

0

1 1

1

Le parcours infixe donne les nombres en ordre croissant !

Nombres (en binaire) .0001 = 1/16, .001= 2/16, .0011 = 3/16, .01= 4/16, .0101 = 5/16, .011 = 6/16, .1 = 8/16, .101 = 9/16, .11 =12/16, .111= 14/16

L'ordre des mots croisés

L'ordre des mots croisés (shortlex en anglais) est défini par u <mc v si et seulement si

- |u| < |v| ou

- |u| = |v| et u <lex v.

Exemples

bar <mc car <mc barda <mc radar <mc abracadabra

Parcours en largeur

9 10 8

4 5 6 7

2 3

1

En largeur :

1, 2, 3, 4, 5, 6, 7, 8, 9, 10 e, 0, 1, 00, 01, 10, 11, 000, 001, 010

0 1

0

0 0

0

1 1

1

Le parcours en largeur est codé par l'ordre des mots croisés.

Remarque : la suite 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010 est croissante.

(10)

Amphi 5 37

Plan

• Arbres et forêts

• Algorithme union-find

• Ordres sur les mots, parcours d'arbres binaires

• Arbres binaires de recherche

Amphi 5 38

Arbre binaire de recherche

Définition : Pour chaque nœud de contenu c, les nœuds du sous-arbre gauche ont un contenu < c et ceux du sous-arbre droit ont un contenu > c.

10 13

8 14 16 21

12 19

15

17

Propriétés des arbres binaires de recherche

• Le parcours infixe (GCD) ordonne les nœuds par contenu croissant.

• Si un nœud a un fils gauche, son prédécesseur est le nœud le plus à droite dans son sous-arbre gauche. Ce prédécesseur n'a pas de fils droit.

• Si un nœud a deux fils, son successeur n'a pas de fils gauche.

10 13

8 14 16 21

12 20

15

17 1

2 3

4 5

6

7

8 9

10

Exemple : prédécesseur de 6

Les arbres binaires en Java

class Arbre { Arbre filsG;

int contenu;

Arbre filsD;

Arbre(Arbre a, int v, Arbre b) {

filsG = a;

contenu = v;

filsD = b;

} }

(11)

Amphi 5 41

Chercher un élément

static Arbre chercher(int x, Arbre a) {

if (a == null || x == a.contenu) return a;

if (x < a.contenu)

return chercher(x, a.filsG);

return chercher(x, a.filsD);

}

Retourne l'arbre dont la racine contient x, ou null.

Amphi 5 42

Chercher (itératif)

static Arbre chercherI(int x, Arbre a) {

while (a != null && x != a.contenu) if (x < a.contenu)

a = a.filsG;

else

a = a.filsD;

return a;

}

Inserer (version destructive)

static Arbre inserer(int x, Arbre a) {

if (a == null)

return new Arbre(null, x, null);

if (x < a.contenu)

a.filsG = inserer(x, a.filsG);

else if (x > a.contenu)

a.filsD = inserer(x, a.filsD);

return a;

}

Inserer (version non destructive)

static Arbre inserer(int x, Arbre a) {

if (a == null)

return new Arbre(null, x, null);

if (x < a.contenu)

return new Arbre(inserer(x,

a.filsG), a.contenu, a.filsD);

else if (x > a.contenu) return new Arbre(a.filsG,

a.contenu, inserer(x, a.filsD));

return a;

}

(12)

Amphi 5 45

Supprimer (1)

(a) Le nœud est une feuille : on l'élimine.

10

8 14 16 21

12 20 15

17 13

10

8 14 16 21

12 20 15

17

Amphi 5 46

Supprimer (2)

(b) Le nœud s a un seul fils t : on élimine s et on

«remonte» t.

10

8 14 21

12 20 15

13 10

8 14 16 21

12 20 15

18 13

19 17

18

19 17

s t

t

Supprimer (3)

(c) Le nœud s a deux fils : on cherche son prédécesseur t (qui n'a pas de fils droit), on remplace le contenu de s par le contenu de t et on élimine t.

s

10

8 13 21

12 20 14

13 10

8 14 16 21

12 20 15

17

16

t

17

Supprimer (destructive)

static Arbre supprimer(int x, Arbre a) {

if (a == null) return a;

if (x == a.contenu)

return supprimerRacine(a);

if (x < a.contenu)

a.filsG = supprimer(x, a.filsG);

else // x > a.contenu a.filsD = supprimer(x, a.filsD);

return a;

}

(13)

Amphi 5 49

Supprimer la racine (récursivité croisée)

static Arbre supprimerRacine(Arbre a) {

if (a.filsG == null) // au plus un fils return a.filsD;

if (a.filsD == null) // idem return a.filsG;

Arbre f = dernierDescendant(a.filsG);

// f est le prédécesseur de a; voir (c) a.contenu = f.contenu; // remplace contenu, // f a au plus un fils

a.filsG = supprimer(f.contenu, a.filsG);

return a;

} Amphi 5 50

Dernier descendant

static Arbre dernierDescendant(Arbre a) {

if (a.filsD == null) return a;

return dernierDescendant(a.filsD);

}

13 10

8 14 16 22

12 20 15

17 21

Le dernier descendant du nœud 15 est le nœud 22

Supprimer la racine (itérative)

static Arbre supprimerRacine(Arbre a){

... // a possède deux fils Arbre b = a.filsG;

if (b.filsD == null) { // cas (i) a.contenu = b.contenu;

a.filsG = b.filsG;

} else { // cas (ii) Arbre p = avantDernierDesc(b);

Arbre f = p.filsD;

a.contenu = f.contenu;

p.filsD = f.filsG;

}

return a;

}

Avant dernier descendant

static Arbre avantDernierDesc(Arbre a) { // a.filsD != null

while (a.filsD.filsD != null) a == a.filsD;

return a;

}

13 10

8 14 16 21

12 19 15

17 20

L'avant dernier descendant du nœud 15 est le nœud 19

(14)

Amphi 5 53

Supprimer la racine (version itérative)

a

10

8 22 25

12 24 21

23

b

cas (i)

10 8

22 25 24 12

23

Amphi 5 54

Supprimer la racine (version itérative)

a

13 10

8 14 22 25

12 24 20

23

p b

16

18

cas (ii)

a

13 10

8 14 22 25

12 24 21

23

p b

16

20

18

f

Performances des arbres de recherche

Si h est la hauteur d'un arbre binaire de recherche, la recherche d'un élément, l'insertion et la suppression sont en O(h).

Le cas le pire est en O(n). Pour éviter ce cas, on utilise des arbres équilibrés : arbres AVL, arbres 2-3, arbres bicolores.

Hauteur moyenne

La hauteur moyenne d'un arbre binaire à n nœuds est en O(n1/2), quand tous les arbres binaires sont équiprobables.

La hauteur moyenne d'un arbre binaire de recherche à n nœuds est en O(log n). La moyenne est calculée sur les suites équiprobables de n entiers insérés dans un arbre initialement vide.

Par exemple les deux suites 2, 1, 3 et 2, 3, 1 produisent le même arbre

binaire de recherche. 1 3

2

Références

Documents relatifs

L’opération estVide teste si la file avec priorité est vide ou pas, l’opération ajouter insère dans la file un nouvel élément avec sa priorité, l’opération premier

Écrivez maintenant la méthode de tri fusion, qui divise la liste en deux listes de taille égale, trie les sous-listes récursivement puis les fusionnent avec la méthode

Chercher sur Internet l‘algorithme DSW (c’est un algorithme qui prend en entrée un arbre de recherche binaire afin de réaliser son équilibrage) et l’algorithme de

L’insertion et la suppression utilisent la rotation ` a gauche et ` a droite d’un arbre lorsqu’il est trop d´ es´ equilibr´ e :.

Complexit´ e des op´ erations pr´ ec´ edentes Pour un arbre binaire de taille n,. la complexit´ e dans le pire des

d- En supposant qu’il existe une fonction point/2 qui permette de dessiner un point de coordonn´ ees (x, y), d´ efinir une fonction qui dessine la repr´ esentation graphique

Dans le cas de points p-q-i alignés (orientation 0), le point le plus éloigné de P est conservé (dans le tableau donné en exemple ci-dessous, c'est la différence des distances PQ

Les pre- mières division d’un arbre sont utilisées pour construire une variable synthétique intégrée à une régression logistique afin de sélectionner les quelques