Langage C et structures de données licence MASS 2004--2005
lundi 4 octobre et mardi 5 octobre 2004 : premiers programmes en C
lundi 11 et mardi 12 octobre 2004
lundi 18 et mardi 19 octobre 2004
lundi 25 et mardi 26 octobre 2004 : tableau de chaînes de caractères et pointeurs
mardi 2 novembre 2004 : passage d'arguments
lundi 8 et mardi 9 novembre 2004 : passage de tableaux, tris
lundi 15 et mardi 16 novembre 2004 : structures
lundi 22 et mardi 23 novembre 2004 : serètcarac ed senîahc, listes chaînées
lundi 29 et mardi 30 novembre 2004 : insertion en fin de liste chaînée, tri fusion de liste chaînée
lundi 6 et mardi 7 décembre 2004 : listes doublement chaînées
lundi 13 et mardi 14 décembre 2004 : listes doublement chaînées attachées aux deux bouts, arbres binaires de recherche
lundi 3 et mardi 4 janvier 2005 : rotations dans les arbres binaires de recherche
lundi 10 et mardi 11 janvier 2005 : AVL
suivant sommaire
lundi 4 octobre et mardi 5 octobre 2004 : premiers programmes en C
bonjour
Le premier programme en C est à ranger dans :
c:\Dev-Cpp\dupont\bonjour.c
Il ne faut pas le mettre dans "Mes Documents", car le chemin d'accès à ce
répertoire contient un blanc, ce qui perturbe l'affichage des messages d'erreur de
compilation.
Il faut donc créer le répertoire dupont
(ou martin si vous vous appelez Martin), dans le répertoire c:\Dev-Cpp.
Il faut sauver le programme dans ce répertoire sous le nom bonjour en précisant
bien que c'est du C (.c) et non pas du C++ (.cpp).
#include
int main()
{ printf("Bonjour");
getchar();
return 0;
}
Pour la compilation et l'exécution, on peut utiliser dans une fenêtre "invite de commande" les commandes :
c:
cd \Dev-Cpp\dupont
..\bin\gcc -Wall -g bonjour.c -o bonjour.exe
bonjour
ou bien
c:\Dev-Cpp\bin\gcc.exe -Wall -g c:\Dev-Cpp\dupont\bonjour.c -o c:\Dev-Cpp\dupont\bonjour.exe
c:\Dev-Cpp\bonjour.exe
Le compilateur gcc (Gnu C Compiler i.e. compilateur C Gnu) lit le fichier bonjour.c qui
contient un programme écrit en C, et fabrique un fichier exécutable bonjour.exe.
L'option -Wall signifie que le compilateur affiche tous (all)
les avertissements (Warnings) possibles.
L'option -g demande au compilateur de mettre dans l'exécutable des informations suplémentaires,
comme les numéros de ligne et les noms de variable du
programme de départ (en C), qui pourront être utilisées par le débogueur.
On peut évidemment utiliser l'environnement de développement dev-c++ pour faire la compilation,
mais il faut aller rajouter -Wall et -g derrière gcc.exe dans la fenêtre
outils/options du compilateur/programmes/gcc
Lorsque l'on exécute le programme, il écrit bonjour dans la fenêtre d'exécution.
Le getchar() est là pour retarder la fin du programme jusqu'à ce qu'on tape la touche Entrée.
Cela évite que la fenêtre ne se referme avant d'avoir eu le temps de lire le "bonjour" que
le programme écrit.
Le programme principal (main en anglais) est en fait une fonction qui rend un nombre entier
(int pour integer en anglais) qui est nul (return 0;) pour dire que
l'exécution s'est bien passée.
#include<stdio.h> est une ligne qui est remplacée par le précompilateur par le
contenu du fichier stdio.h qui contient les entêtes (Header en anglais) des fonctions
et procédures normalisées (STanDard signifie norme en anglais) pour les entrées (Input) et
sorties (Output).
Si on oublie cette ligne, le compilateur ne reconnaît pas printf et getchar.
21+7=28
#include
int mini(int a,int b) {return ab ? a : b;}
int pgcd(int a,int b) {return b ? pgcd(b,a%b) : a;}
int Pgcd(int a,int b) {while(b) {int c=a%b; a=b; b=c;} return a;}
int main()
{ int a,b;
printf("Donnez deux nombres : ");
scanf("%d%d",&a,&b);
printf("%d*%d=%d\n",a,b,a*b); // produit
printf("%d/%d=%d\n",a,b,a/b); // quotient
printf("%d%%%d=%d\n",a,b,a%b); // reste
printf("%d+%d=%d\n",a,b,a+b); // somme
printf("%d-%d=%d\n",a,b,a-b); // différence
printf("%d>>%d=%d\n",a,b,a>>b); // décalage à droite
printf("%d<<%d=%d\n",a,b,a<
réels : float, double et long double
Ce programme utilise des réels en double précisions (sur 8 octets, avec 15 chiffres décimaux
significatifs.)
#include
#include
int main()
{ double r;
printf("Quel est le rayon du cercle ? ");
scanf("%lf",&r);
printf("Le diamètre est %20.15lf\n",2*r);
printf("La circonférence est %20.15lf\n",2*r*M_PI);
printf("La surface est %20.15lf\n",r*r*M_PI);
getchar();
getchar();
return 0;
}
Ce programme utilise des réels sur 10 octets, avec 19 chiffres décimaux significatifs.)
#include
#include
int main()
{ long double r;
const long double pi=acosl(-1); // M_PI est un double
printf("Quel est le rayon du cercle ? ");
scanf("%Lf",&r);
printf("Le diamètre est %30.19Lf\n",2*r);
printf("La circonférence est %30.19Lf\n",2*r*pi);
printf("La surface est %30.19Lf\n",r*r*pi);
getchar();
getchar();
return 0;
}
float double long double
format %f %lf %Lf
taille (en octets) 4 8 10
précision (nombre de chiffres décimaux) 6 15 19
fonction sinus sinf sin sinl
entiers signés ou entiers non signés ? (signed int or unsigned int ?)
Le programme suivant écrit en C++ permet de comparer les affichages
en C (printf) et en C++ (cout<<).
Il faut l'appeler
c:\Dev-Cpp\dupont\affiche.cpp
Il se compile par
g++ -Wall -g affiche.cpp -o affiche.exe
Il ne faut donc pas oublier de rajouter -Wall et -g derrière g++.exe
dans la bonne fenêtre.
#include<stdio.h> sert à reconnaître printf
et toutes les procédures d'entrée-sortie du C.
#include<iostream> sert à reconnaître cout
et toutes les procédures d'entrée-sortie du C++.
En fait cout est dans le ``namespace'' std.
Si on n'avait pas mis using namespace std; ,
il aurait fallu mettre std::cout au lieu de cout.
#include
#include
using namespace std;
int main()
{ printf("%d %d %u %u\n",-1,-1u,-1,-1u);
cout<<-1<<' '<<-1u<<'\n';
getchar();
return 0;
}
Ce programme écrit :
-1 -1 4294967295 4294967295
-1 4294967295
On peut noter qu'en C++, -1 qui est signé est écrit -1, et -1u qui est non signé est écrit
(2**32)-1.
En C, -1 et -1u sont écrits tous les deux de la même manière -1 quand on utilise le format
%d qui veut dire Décimal (c'est-à-dire en base 10) signé.
Ils sont écrits tous les deux de la même manière (2**32)-1 quand on utilise le format
%u qui veut dire décimal non signé (Unsigned).
En fait ils sont tous les deux représentés de la même manière dans l'ordinateur, par un mot de 4 octets,
c'est-à-dire 32 bits, chacun des 32 bits valant 1.
Bit signifie BInary digIT, c'est-à-dire chiffre binaire, c'est-à-dire chiffre en base 2.
Autrement dit, en français, -1u est un nombre représenté en base 2 avec 32 fois le chiffre 1.
-1 aussi.
Dans l'ordinateur, les nombres sont représentés en binaires, ils ne sont convertis en décimal
qu'au moment de l'affichage.
expressions et instructions
valeur effet
5 5 aucun
5+2 7 aucun
a valeur de a aucun
a+3 (valeur de a)+3 aucun
a=4 4 la valeur de a devient 4
i++ ancienne valeur de i la valeur de i augmente de 1
++i nouvelle valeur de i la valeur de i augmente de 1
(a=4)+3 7 la valeur de a devient 4
a=i++ ancienne valeur de i la valeur de i augmente de 1 et a prend l'ancienne valeur de i
sont des expressions. Lors qu'on les évalue, on obtient leur valeur qui pourra être passée à une
procédure ou utilisée comme test (dans un if ou un while), ou utilisée comme opérande d'un opérateur
(si cette expression est une sous expression d'une expression plus compliquée) ou encore, purement
et simplement ignorée si l'expression est suivie d'un point-virgule.
Lors de l'évaluation d'une expression, c'est-à-dire du calcul de sa valeur, il peut y avoir
des effets de bord, c'est-à-dire que des variables changent de valeur.
5;
5+2;
a;
a+3;
sont des intructions valides qui n'ont aucun effet. Le programme se comportera de la même
manière si on les enlève. Elles sont donc inutiles et rendent le programme moins lisibles.
Ce n'est sans doute pas ce qu'on voulait mettre dans le programme.
Le compilateur ne signale donc pas d'erreur, mais un avertissement (warning).
a=4; met 4 dans a
(a=4)+3; est une instruction équivalente à a=4; Il vaut mieux écrire a=4;
i++; et ++i; sont des instructions équivalentes qui augmentent i de 1.
suivant précédent sommaire
lundi 11 et mardi 12 octobre 2004
Priorité des opérateurs
Les opérateurs binaires ``ordinaires'' +, -, *, /, %,
<<, >>, |, &, ^, ==, !=,
<, >, <= et >=
évaluent leur deux opérandes dans un ordre laissé à la discrétion du compilateur.
Par exemple a=2,(a=4)+(a++) a
pour valeur 8 et pour effet de mettre 5 dans a si a=4 est évalué avant a++ et
pour valeur 6 et pour effet de mettre 4 dans a si a=4 est évalué après a++.
Mais on pourrait aussi avoir comme valeur 6 et comme effet de mettre 5 dans a.
De même l'expression a=3,a++*a++ a comme effet de mettre 5 dans a
et pour valeur 12 ou 9.
En fait selon la norme ANSI, de telles
expressions
ont un résultat et un effet qui sont indéterminés.
Elles pourraient par exemple valoir 1000 et remettre à 0 toutes les autres variables du programme ...
Par contre les opérateurs virgule, &&, || et ?:
évaluent d'abord leur premier opérande.
Les opérateurs ``et logique'' et ``ou logique'' n'évaluent pas leur second opérande si la valeur
du premier suffit pour déterminer son résultat.
Par exemple l'évaluation de 2==3 && i++ ne modifie pas la valeur de i
tandis que celle de 2==2 && i++ augmente i de 1.
L'opérateur ternaire ?: n'évalue qu'un seul de ses deux derniers opérandes.
Par exemple a=3?i++:j++; augmente i de 1 et
met son ancienne valeur dans a mais ne modifie pas j.
a=2,b=i==3?*t++[4+a++]->x1.x2,4:5,a+b;
---
4+ 2
6
------------------,4
----? 4 :5
2 ,b= (4 ou 5) , 7
2 , (4 ou 5) , 7
a prend d'abord la valeur 2.
Puis si i vaut 3 alors on évalue l'expression *t++[4+a++]->x1.x2
En fait t pointe dans un tableau dont les éléments sont des pointeurs sur une structure qui
possède un champ x1 qui est une structure qui possède un champ x2 qui est un pointeur.
En effet on part de t,
on lui applique l'opérateur postfixe ++,
puis l'opérateur postfixe [6],
puis l'opérateur postfixe ->x1,
puis l'opérateur postfixe .x2,
puis l'opérateur préfixe *,
et on ne fait rien du résultat puisque c'est l'opérande gauche de l'opérateur binaire virgule.
Donc l'évaluation de cette expression a pour effet de faire pointer t une case plus en avant
et de faire passer a à 3.
Après cela on met 4, qui est la valeur de l'opération virgule dans b.
Si i ne valait pas 3 alors a et t ne changent pas de valeur,
et on met 5 dans b.
Après tous cela on évalue a+b qui vaut 3+4 ou 2+5, c'est-à-dire de toute façon 7,
mais on ne fait rien de ce 7.
L'instruction précédente est donc équivalente à
{a=2; if(i==3) {t++; a++; b=4;} else b=5;} }
for=while
Un for en C est complètement équivalent à un while.
Par exemple :
for(i=3;i<=10;i++) s+=i;
est équivalent à
{i=3; while(i<=10) {s+=i;i++;}}
ou à
{i=3; while(i<=10) s+=i,i++;}
ou encore à
{i=3; while(i<=10) s+=i++;}
De même
while(condition) instruction
est équivalent à
for(;condition;) instruction
mardi 12 octobre : boucles for et tableaux
#include
int main()
{ int n,i,j;
printf("Combien de nombres ? ");
scanf("%d",&n);
for(i=1;i<=n;++i) printf("%d ",i);
printf("\n");
for(i=n;i;--i) printf("%d ",i);
printf("\n");
for(i=1;i<=n;++i,printf("\n")) for(j=1;j<=n;++j) printf("%4d",i*j);
for(i=1;i<=n;++i,printf("\n")) for(j=1;j<=i;++j) printf("%4d",1 );
int t[n][n];
for(i=0;i=0;--j)
printf("%4d",u[j]= !j || j==i ? 1 : u[j]+u[j-1]);
return 0;
}
Combien de nombres ? 5
1 2 3 4 5
5 4 3 2 1
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
1
1 1
1 1 1
1 1 1 1
1 1 1 1 1
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
suivant précédent sommaire
lundi 18 et mardi 19 octobre 2004
passage de fonction en argument à une procédure
#include
typedef int typef1(int);
void testf1(typef1 f)
{ int i;
for(i=1;i<=10;++i) printf("%2d %d\n",i,f(i));
}
int add2(int x){return x+2;}
int mul2(int x){return x*2;}
int main()
{ testf1(add2);
testf1(mul2);
return 0;
}
Le programme précédent écrit
1 3
2 4
3 5
4 6
5 7
6 8
7 9
8 10
9 11
10 12
1 2
2 4
3 6
4 8
5 10
6 12
7 14
8 16
9 18
10 20
copie de chaînes de caractères
#include
#include
int main()
{ char t[]="Il fait beau.", s[strlen(t)+1];
int i,j;
for(i=0;(s[i]=t[i]);++i) ;
printf("%s\n%s\n",t,s);
for(i=0;(s[i]=t[i]==' '?',':t[i]);++i) ;
printf("%s\n",s);
for(i=j=0;t[i]==' ' || (s[j++]=t[i]);++i) ;
printf("%s\n",s);
return 0;
}
Le programme précédent écrit
Il fait beau.
Il fait beau.
Il,fait,beau.
Ilfaitbeau.
comparaison de chaînes de caractères
#include
#include
#include
int main()
{ char *t[]={"Jules","Antoine","Paul","Simon","Adrien","Paul","Vincent",0};
int i,j;
for(i=0;t[i];++i) for(j=i+1;t[j];j++) if(!strcmp(t[i],t[j]))
printf("%s apparaît deux fois en positions %d et %d.\n",t[i],i,j), exit(0);
printf("Tous les noms sont différents.\n");
return 0;
}
Le programme précédent écrit
Paul apparaît deux fois en positions 2 et 5.
suivant précédent sommaire
lundi 25 octobre 2004 : tableau de chaînes de caractères
#include
#include
#include
int main()
{ char *t[]={"Jules","Antoine","Paul","Simon","Adrien","Paul","Vincent",0},*x,c;
int i,j,l;
for(i=0 ;t[i];++i)
for(j=i+1;t[j];++j)
if(!strcmp(t[i],t[j]))
{ printf("%s apparaît deux fois en positions %d et %d.\n",t[i],i,j);
goto suite;
}
printf("Tous les noms sont différents.\n");
suite:
for(i=0;t[i];++i) printf("%s ",t[i]);
printf("\n");
for(i=0 ;t[i];++i)
for(j=i+1;t[j];++j)
if(strcmp(t[i],t[j])>0) x=t[i],t[i]=t[j],t[j]=x;
for(i=0;t[i];++i) printf("%s ",t[i]);
printf("\n");
for(i=0;t[i];++i) t[i]=strcpy(malloc(strlen(t[i])+1),t[i]);
for(i=0;t[i] ;++i)
for(j=0;t[i][j];++j)
if(t[i][j]=='e') t[i][j]='E';
for(i=0;t[i];++i) printf("%s ",t[i]);
printf("\n");
for(i=0;t[i];++i)
for(j=0,l=strlen(t[i])-1;j
Le programme précédent écrit
Paul apparaît deux fois en positions 2 et 5.
Jules Antoine Paul Simon Adrien Paul Vincent
Adrien Antoine Jules Paul Paul Simon Vincent
AdriEn AntoinE JulEs Paul Paul Simon VincEnt
nEirdA EniotnA sEluJ luaP luaP nomiS tnEcniV
mardi 26 octobre 2004 : pointeurs
pointeurs et chaînes de caractères : abracadabra
#include
#include
#define abra "abracadabra"
int main()
{ char s[]=abra, t[]=abra, *x,*y,c;
for(x=s,y=s+strlen(s)-1;x*y) c=*x,*x=*y,*y=c;
printf("%s\n",s);
return 0;
}
Le programme précédent écrit
arbadacarba
abracadabra
bracadabra
racadabra
acadabra
cadabra
adabra
dabra
abra
bra
ra
a
abracadabra
abracadabr
abracadab
abracada
abracad
abraca
abrac
abra
abr
ab
a
aaaaabbcdrr
pointeurs et tableaux d'entiers
#include
int som(int *t,int n) {int s=0; for(;n;--n) s+=*t++; return s;}
void aff(int *t,int n) {for(;n;--n) printf("%d ",*t++); }
#define ex(a) printf(#a" : "),a,printf("\n")
#define ev(a) printf(#a" : %d\n",a)
int main()
{ const int n=10;
int t[n], i;
for(i=0;i
Le programme précédent écrit
aff(t,n) : 0 3 6 9 2 5 8 1 4 7
aff(t+3,4) : 9 2 5 8
som(t,n) : 45
som(t+3,4) : 24
suivant précédent sommaire
mardi 2 novembre 2004 : passage d'arguments
#include
#include
void cercle(double rayon,double *diametre,double *circonference,double *surface)
{ *diametre=2*rayon;
*circonference=*diametre*M_PI;
*surface=rayon*rayon*M_PI;
}
int main()
{ double r,d,c,s;
printf("Rayon ? ");
scanf("%lf",&r);
cercle(r,&d,&c,&s);
printf("Diamètre : %lf\nCirconférence : %lf\nSurface : %lf\n",d,c,s);
return 0;
}
Le programme précédent écrit
Rayon ? 5
Diamètre : 10.000000
Circonférence : 31.415927
Surface : 78.539816
Si on l'écrit en C++, on peut utiliser le passge d'argument par référence :
#include
#include
void cercle(double rayon,double &diametre,double &circonference,double &surface)
{ diametre=2*rayon;
circonference=diametre*M_PI;
surface=rayon*rayon*M_PI;
}
int main()
{ double r,d,c,s;
printf("Rayon ? ");
scanf("%lf",&r);
cercle(r,d,c,s);
printf("Diamètre : %lf\nCirconférence : %lf\nSurface : %lf\n",d,c,s);
return 0;
}
#include
#include
double arith(double a,double b) {return (a+b)/2;}
double geom (double a,double b) {return sqrt(a*b);}
double harm (double a,double b) {return 2*a*b/(a+b);}
double arithgeom(double a,double b)
{ double c=arith(a,b), d=geom(a,b);
return a==c || b==d ? a : arithgeom(c,d);
}
typedef double moy(double,double);
double moymm(double a,double b,moy m1,moy m2)
{ double c=m1(a,b), d=m2(a,b);
return a==c || b==d ? a : moymm(c,d,m1,m2);
}
void testmoy(double a,double b)
{ printf("moyennes de %lf et %lf\n",a,b);
printf("arithmétique : %lf\n",arith(a,b));
printf("harmonique : %lf\n",harm(a,b));
printf("géométrique : %lf = %lf\n",geom(a,b),moymm(a,b,arith,harm));
printf("arithméticogéométrique : %lf = %lf =%lf\n",arithgeom(a,b),moymm(a,b,arith,geom),
1/moymm(1/a,1/b,geom,harm));
}
int main()
{ testmoy(1,2);
testmoy(2,4);
testmoy(1,4);
return 0;
}
Le programme précédent écrit
moyennes de 1.000000 et 2.000000
arithmétique : 1.500000
harmonique : 1.333333
géométrique : 1.414214 = 1.414214
arithméticogéométrique : 1.456791 = 1.456791 =1.456791
moyennes de 2.000000 et 4.000000
arithmétique : 3.000000
harmonique : 2.666667
géométrique : 2.828427 = 2.828427
arithméticogéométrique : 2.913582 = 2.913582 =2.913582
moyennes de 1.000000 et 4.000000
arithmétique : 2.500000
harmonique : 1.600000
géométrique : 2.000000 = 2.000000
arithméticogéométrique : 2.243029 = 2.243029 =2.243029
suivant précédent sommaire
lundi 8 et mardi 9 novembre 2004 : passage de tableaux, tris
#include
/*
void afft(int n,int t[n])
{ int i;
for(i=0;ip) f--;
if(d>=f) break;
x=*d,*d=*f,*f=x;
}
trirapide(d-t,t);
trirapide(t+n-f-1,f+1);
}
void testtri(void tri(int,int[]))
{ int t[20], i ,x=1;
for(i=0;i<20;i++) t[i]=x=3*x%100;
afft(20,t);
tri (20,t);
afft(20,t);
}
#define aff0(a) printf(#a" : "),a
#define affd(a) printf(#a" : %d\n",(a))
int main()
{ int t[20], i ,x=1;
for(i=0;i<20;i++) t[i]=x=3*x%100;
aff0(afft(20,t));
aff0(afft(4,t+3));
aff0(retourne(7,t+5));
aff0(afft(20,t));
affd(prod(5,t));
affd(somm(3,t+3));
affd(maxt(10,t+6));
testtri(triselection1);
testtri(triselection2);
testtri(trifusion);
testtri(trirapide);
return 0;
}
Le programme précédent écrit
afft(20,t) : 3 9 27 81 43 29 87 61 83 49 47 41 23 69 7 21 63 89 67 1
afft(4,t+3) : 81 43 29 87
retourne(7,t+5) : afft(20,t) : 3 9 27 81 43 41 47 49 83 61 87 29 23 69 7 21 63 89 67 1
prod(5,t) : 2539107
somm(3,t+3) : 165
maxt(10,t+6) : 87
3 9 27 81 43 29 87 61 83 49 47 41 23 69 7 21 63 89 67 1
1 3 7 9 21 23 27 29 41 43 47 49 61 63 67 69 81 83 87 89
3 9 27 81 43 29 87 61 83 49 47 41 23 69 7 21 63 89 67 1
1 3 7 9 21 23 27 29 41 43 47 49 61 63 67 69 81 83 87 89
3 9 27 81 43 29 87 61 83 49 47 41 23 69 7 21 63 89 67 1
1 3 7 9 21 23 27 29 41 43 47 49 61 63 67 69 81 83 87 89
3 9 27 81 43 29 87 61 83 49 47 41 23 69 7 21 63 89 67 1
1 3 7 9 21 23 27 29 41 43 47 49 61 63 67 69 81 83 87 89
Le tri rapide a été fait le lundi 15.
suivant précédent sommaire
lundi 15 et mardi 16 novembre 2004 : structures
#include
#include
#include
typedef struct personne personne, *pp;
struct personne{char *prenom, sexe; pp pere,mere;};
pp t[9];
void init()
{ int i; // antoine jean+julie
for(i=0;i<9;i++) t[i]=malloc(sizeof(personne)); // | +-------+------+
const pp antoine=t[0], jeanne =t[1], henri =t[2],// jeanne+hubert hector aurelie
hubert =t[3], hector =t[4], aurelie=t[5],// | |
claire =t[6], jean =t[7], julie =t[8];// henri claire
antoine->prenom="antoine"; antoine->pere=0; antoine->mere=0;
jeanne ->prenom="jeanne" ; jeanne ->pere=antoine; jeanne ->mere=0;
henri ->prenom="henri" ; henri ->pere=hubert; henri ->mere=jeanne;
hubert ->prenom="hubert" ; hubert ->pere=jean; hubert ->mere=julie;
hector ->prenom="hector" ; hector ->pere=jean; hector ->mere=julie;
aurelie->prenom="aurelie"; aurelie->pere=jean; aurelie->mere=julie;
claire ->prenom="claire" ; claire ->pere=0; claire ->mere=aurelie;
jean ->prenom="jean" ; jean ->pere=0; jean ->mere=0;
julie ->prenom="julie" ; julie ->pere=0; julie ->mere=0;
}
void sexualisation()
{ int i;
for(i=0;i<9;i++) t[i]->sexe='i';
for(i=0;i<9;i++)
{ if(t[i]->pere) t[i]->pere->sexe='m';
if(t[i]->mere) t[i]->mere->sexe='f';
}
}
void affpp(pp a)
{ printf("%s(%c)",a->prenom,a->sexe);
if(a->mere) printf(" Sa mere est %s." ,a->mere->prenom);
if(a->pere) printf(" Son pere est %s.",a->pere->prenom);
printf("\n");
}
void afft() {int i; printf("\n"); for(i=0;i<9;i++) affpp(t[i]);}
void trichrono()
{ int i,j;
pp x;
for(i=0;i<9;i++)
for(j=i;++j<9;)
if(t[i]->pere==t[j] || t[i]->mere==t[j]) x=t[i],t[i]=t[j],t[j]=x,j=i;
}
void trialpha()
{ int i,j;
pp x;
for(i=0;i<9;i++)
for(j=i;++j<9;)
if(strcmp(t[i]->prenom,t[j]->prenom)>0) x=t[i],t[i]=t[j],t[j]=x;
}
int main()
{ init(); sexualisation();
afft();
trichrono();
afft();
trialpha();
afft();
trichrono();
afft();
return 0;
}
Le programme précédent écrit
antoine(m)
jeanne(f) Son pere est antoine.
henri(i) Sa mere est jeanne. Son pere est hubert.
hubert(m) Sa mere est julie. Son pere est jean.
hector(i) Sa mere est julie. Son pere est jean.
aurelie(f) Sa mere est julie. Son pere est jean.
claire(i) Sa mere est aurelie.
jean(m)
julie(f)
antoine(m)
jeanne(f) Son pere est antoine.
jean(m)
julie(f)
hector(i) Sa mere est julie. Son pere est jean.
aurelie(f) Sa mere est julie. Son pere est jean.
claire(i) Sa mere est aurelie.
hubert(m) Sa mere est julie. Son pere est jean.
henri(i) Sa mere est jeanne. Son pere est hubert.
antoine(m)
aurelie(f) Sa mere est julie. Son pere est jean.
claire(i) Sa mere est aurelie.
hector(i) Sa mere est julie. Son pere est jean.
henri(i) Sa mere est jeanne. Son pere est hubert.
hubert(m) Sa mere est julie. Son pere est jean.
jean(m)
jeanne(f) Son pere est antoine.
julie(f)
antoine(m)
jean(m)
julie(f)
hector(i) Sa mere est julie. Son pere est jean.
hubert(m) Sa mere est julie. Son pere est jean.
jeanne(f) Son pere est antoine.
aurelie(f) Sa mere est julie. Son pere est jean.
henri(i) Sa mere est jeanne. Son pere est hubert.
claire(i) Sa mere est aurelie.
suivant précédent sommaire
lundi 22 novembre 2004 : serètcarac ed senîahc
#include
#include
#include
char*concat(char*s,char*t)
{ char *p=malloc(strlen(s)+strlen(t)+1), *q=p;
while(*s) *q++=*s++;
while(*t) *q++=*t++;
*q=0;
return p;
}
char*retourne(char*s)
{ int l=strlen(s);
char *p=malloc(l+1), *q=p;
for(s+=l;l;l--) *q++=*--s;
*q=0;
return p;
}
char*retourne2(char*s)
{ int l=strlen(s);
char *p=malloc(l+1)+l;
*p=0;
while(*s) *--p=*s++;
return p;
}
char*maj(char*s)
{ char *p=malloc(strlen(s)+1), *q;
for(q=p;*s;s++,q++) *q=*s>='a' && *s<='z' ? *s+'A'-'a' : *s;
return p;
}
int main()
{ char *s="Esope reste ici et se repose.", *t="Tu l'as trop écrasé, César, ce port-salut.";
printf("%s%s\n","bon","jour");
printf("%s\n",concat("bon","jour"));
printf("%s<-->%s\n",s,retourne(s));
printf("%s<-->%s\n",t,retourne2(t));
printf("%s %s\n",maj(s),maj(t));
return 0;
}
Le programme précédent écrit
bonjour
bonjour
Esope reste ici et se repose.<-->.esoper es te ici etser eposE
Tu l'as trop écrasé, César, ce port-salut.<-->.tulas-trop ec ,raséC ,ésarcé port sa'l uT
ESOPE RESTE ICI ET SE REPOSE. TU L'AS TROP éCRASé, CéSAR, CE PORT-SALUT.
mardi 23 novembre 2004 : listes chaînées
liste, aff, nouv, copie,
somre, somit et add1 dans le programme suivant.
suivant précédent sommaire
lundi 29 novembre 2004 : insertion en fin de liste chaînée
retourne, retour, affrec, ffa,
lliste, ajoutetete, ajoutefin et enlevetete
dans le programme suivant.
mardi 30 novembre 2004 : tri fusion de liste chaînée
#include
#include
typedef struct chainon chainon, *liste;
struct chainon{int val; liste suite;};
void aff(liste l)
{ for(;l;l=l->suite) printf("%d ",l->val);
printf("\n");
}
liste nouv(int val, liste suite)
{ liste a=malloc(sizeof(*a));
a->val=val;
a->suite=suite;
return a;
}
liste copie(liste l) {return l ? nouv(l->val,copie(l->suite)) : 0;}
int somre(liste l) {return l ? l->val+somre(l->suite) : 0;}
int somit(liste l)
{ int s=0;
for(;l;l=l->suite) s+=l->val;
return s;
}
void add1(liste l) {for(;l;l=l->suite) l->val++;}
liste eipoc(liste l)
{ liste p=0;
for(;l;l=l->suite) p=nouv(l->val,p);
return p;
}
liste retourne(liste l)
{ liste p=0;
while(l)
{ liste q=l; l=l->suite, // on enleve un chainon q en tete de l
q->suite=p, p=q; // on le met en tete de p
}
return p;
}
void retour(liste *l) {*l=retourne(*l);}
void affrec(liste l) {if(l) printf("%d ",l->val), affrec(l->suite);}
void ffa (liste l) {if(l) ffa (l->suite), printf("%d ",l->val);}
typedef struct {liste tete,fin;} lliste;
// tete pointe sur le premier chainon et fin sur le dernier.
// La liste est vide ssi tete==0, et dans ce cas fin n'est pas défini.
void ajoutetete(lliste *a,int x)
{ a->tete=nouv(x,a->tete);
if(!a->tete->suite) a->fin=a->tete; // la liste etait vide
}
void ajoutefin(lliste *a,int x)
{ if(!a->tete) return ajoutetete(a,x); // la liste est vide
a->fin->suite=nouv(x,0);
a->fin=a->fin->suite;
}
int enlevetete(lliste *a,int *x)
{ if(!a->tete) return 0;
liste l=a->tete;
*x =l->val;
a->tete=l->suite;
free(l);
return 1;
}
liste fusionrec(liste a,liste b)
{ return !a ? b :
!b ? a :
b->val>a->val ? a->suite=fusionrec(a->suite,b), a :
( b->suite=fusionrec(b->suite,a), b );
}
liste fusionit(liste a,liste b)
{ liste c,r;
if(!a) return b;
if(!b) return a;
if(b->val>a->val) c=r=a, a=a->suite;
else c=r=b, b=b->suite;
while(a && b)
if(b->val>a->val) c->suite=a, a=a->suite, c=c->suite;
else c->suite=b, b=b->suite, c=c->suite;
c->suite=a ? a : b;
return r;
}
liste fusionit2(liste a,liste b)
{ liste c, *l=&c;
while(a && b) if(b->val>a->val) *l=a, l=&a->suite, a=*l;
else *l=b, l=&b->suite, b=*l;
*l=a ? a : b;
return c;
}
#define fusion fusionit2
liste trifusion(liste l)
{ liste p=0,r=0;
while(l)
{ liste q=l; l=q->suite, // on enleve un chainon q en tete de l
q->suite=p, p=r, r=q; // on le met en tete de p, puis on echange p et r
}
return !p ? r : fusion(trifusion(p),trifusion(r));
}
int main(){ liste l=nouv(3,nouv(7,nouv(5,nouv(1,0)))), k=copie(l), m=l;
affrec(l), ffa(l), printf("\n");
aff(l), aff(k), aff(m);
printf("%d=%d\n",somre(l),somit(l));
add1(l);
aff(l), aff(k), aff(m=eipoc(m));
lliste ll={0,0};
int x,y;
for(x=0;x<10;x++) (x&1 ? ajoutetete : ajoutefin)(&ll,x), aff(ll.tete);
while(enlevetete(&ll,&x) && enlevetete(&ll,&y)) ajoutefin(&ll,x+y-3), aff(ll.tete);
for(x=1;x;x=(x*2+1)%11) l=nouv(x,l);
aff(l);
retour(&l);
aff(l);
aff(l=trifusion(l));
return 0;
}
Le programme précédent écrit
3 7 5 1 1 5 7 3
3 7 5 1
3 7 5 1
3 7 5 1
16=16
4 8 6 2
3 7 5 1
2 6 8 4
0
1 0
1 0 2
3 1 0 2
3 1 0 2 4
5 3 1 0 2 4
5 3 1 0 2 4 6
7 5 3 1 0 2 4 6
7 5 3 1 0 2 4 6 8
9 7 5 3 1 0 2 4 6 8
5 3 1 0 2 4 6 8 13
1 0 2 4 6 8 13 5
2 4 6 8 13 5 -2
6 8 13 5 -2 3
13 5 -2 3 11
-2 3 11 15
11 15 -2
-2 23
18
5 2 6 8 9 4 7 3 1 4 8 6 2
2 6 8 4 1 3 7 4 9 8 6 2 5
1 2 2 3 4 4 5 6 6 7 8 8 9
suivant précédent sommaire
lundi 6 et mardi 7 décembre 2004 : listes doublement chaînées
lundi 6 décembre 2004 : anneaux
#include
#include
typedef struct chainon *anneau;
struct chainon{int val; anneau suite,prec;};
anneau nouv(int val)
{ anneau a=malloc(sizeof(*a));
a->val=val;
return a->suite=a->prec=a;
}
void croise(anneau a,anneau b)
{ anneau c;
c=a->suite, a->suite=b->suite, b->suite=c;
a->suite->prec=a;
b->suite->prec=b;
}
void aff(anneau a)
{ anneau b=a;
do printf(" %d",a->val), a=a->suite; while(a!=b);
printf("\n");
}
anneau copie(anneau a)
{ anneau b=a, x=nouv(a->val);
for(;a=a->prec,a!=b;) croise(x,nouv(a->val));
return x;
}
int longueur(anneau a)
{ int j=0;
anneau b=a;
do j++, a=a->suite; while(a!=b);
return j;
}
int somme(anneau a)
{ int j=0;
anneau b=a;
do j+=a->val, a=a->suite; while(a!=b);
return j;
}
void retourne1(anneau a)
{ anneau b=a->suite, c;
while(c=b->suite,c!=a) croise(b,c), croise(a,c); // a x ... b c y ... -> a c x ... b y ...
}
void retourne2(anneau a)
{ anneau b=a, c;
do c=a->suite, a->suite=a->prec, a->prec=c, a=c; while(a!=b);
}
void add1(anneau a)
{ anneau b=a;
do a->val++, a=a->suite; while(a!=b);
}
int memeanneau(anneau a,anneau b)
{ anneau c=a;
for(;;) if(a==b) return 1; else if(a=a->suite, a==c) return 0;
}
int main()
{ int j;
anneau a=nouv(3);
for(j=4;j<10;j++) croise(a,nouv(j));
aff(a);
anneau b=copie(a);
aff(b);
retourne1(a);
aff(a);
retourne2(a);
aff(a);
printf("longueur : %d somme : %d\n",longueur(a),somme(a));
add1(a);
aff(a);
printf("%d %d\n",memeanneau(a,a->prec), memeanneau(a,b));
return 0;
}
Le programme précédent écrit
3 9 8 7 6 5 4
3 9 8 7 6 5 4
3 4 5 6 7 8 9
3 9 8 7 6 5 4
longueur : 7 somme : 42
4 10 9 8 7 6 5
1 0
mardi 7 décembre 2004 : listes triées terminées par des pointeurs nuls
#include
#include
typedef struct chainon *liste;
struct chainon{int val; liste suite,prec;};
void aff(liste p)
{ for(;p;p=p->suite) printf("%d ",p->val);
printf("\n");
}
liste inseretrie(liste l,int val)
{ liste p=0, s=l, x=malloc(sizeof(*x));
while(s && val>s->val) p=s, s=s->suite;
x->val=val;
x->suite=s;
x->prec =p;
if(p) p->suite=x; else l=x;
if(s) s->prec =x;
return l;
}
liste otetrie(liste l,int val)
{ liste s=l;
while(s && val>s->val) s=s->suite;
if(!s || s->val!=val) return l;
if(s->prec ) s->prec->suite=s->suite; else l=s->suite;
if(s->suite) s->suite->prec=s->prec;
free(s);
return l;
}
int main()
{ int j, t[]={3,6,1,4,10,5};
liste x=0;
for(j=0;j<6;j++) x=inseretrie(x,t[j]), aff(x);
for(j=0;j<6;j++) x= otetrie(x,t[j]), aff(x);
return 0;
}
Le programme précédent écrit
3
3 6
1 3 6
1 3 4 6
1 3 4 6 10
1 3 4 5 6 10
1 4 5 6 10
1 4 5 10
4 5 10
5 10
5
suivant précédent sommaire
lundi 13 décembre 2004 : listes doublement chaînées attachées aux deux bouts
#include
#include
typedef struct chainon *liste;
struct chainon{int val; liste suite,prec;};
liste nouv()
{ liste a=malloc(sizeof(*a));
return a->suite=a->prec=a;
}
void aff(liste a)
{ liste p;
for(p=a->suite;p!=a;p=p->suite) printf("%d ",p->val);
printf("\n");
}
void inseretrie(liste l,int val)
{ liste s=l->suite, x=malloc(sizeof(*x));
while(s!=l && val>s->val) s=s->suite;
x->val=val;
x->suite=s;
x->prec =s->prec;
x->prec->suite=x;
s->prec =x;
}
void otetrie(liste l,int val)
{ liste s=l->suite;
while(s!=l && val>s->val) s=s->suite;
if(s==l || s->val!=val) return;
s->prec->suite=s->suite;
s->suite->prec=s->prec;
free(s);
}
liste copie1(liste a)
{ liste b=nouv(), p;
for(p=a->prec;p!=a;p=p->prec) inseretrie(b,p->val);
return b;
}
liste copie2(liste a)
{ liste b=malloc(sizeof(*b)), p, q, r=b;
for(p=a->suite;p!=a;p=p->suite)
q=malloc(sizeof(*q)), q->val=p->val, q->prec=r, r->suite=q, r=q;
b->prec=r, r->suite=b;
return b;
}
#define copie copie2
liste fusion(liste a,liste b)
{ liste c=nouv(), p=a->prec, q=b->prec;
while(p!=a && q!=b) if(p->val>q->val) inseretrie(c,p->val), p=p->prec;
else inseretrie(c,q->val), q=q->prec;
while(p!=a ) inseretrie(c,p->val), p=p->prec;
while( q!=b) inseretrie(c,q->val), q=q->prec;
return c;
}
int main()
{ int j, t[]={3,6,1,4,10,5};
liste x=nouv(), y, z=nouv(), u;
for(j=0;j<6;j++) inseretrie(x,t[j]), aff(x);
y=copie(x); aff(y);
for(j=0;j<6;j++) otetrie(x,t[j]), aff(x), inseretrie(z,t[j]^3);
aff(z);
u=fusion(y,z); aff(u);
return 0;
}
Le programme précédent écrit
3
3 6
1 3 6
1 3 4 6
1 3 4 6 10
1 3 4 5 6 10
1 3 4 5 6 10
1 4 5 6 10
1 4 5 10
4 5 10
5 10
5
0 2 5 6 7 9
0 1 2 3 4 5 5 6 6 7 9 10
mardi 14 décembre 2004 : arbres binaires de recherche
#include
#include
typedef struct noeud *abr;
struct noeud{int val,eq;abr fg,fd;};
void aff(abr a)
{ if(a) aff(a->fg), printf("%d ",a->val), aff(a->fd);}
void affln(abr a) {aff(a), printf("\n");}
abr nouv(abr fg,int val,abr fd)
{ abr a=malloc(sizeof(*a));
a->val=val;
a->fg =fg;
a->fd =fd;
a->eq =0;
return a;
}
abr f(int val) {return nouv(0,val,0);}
abr recherche(abr a,int val)
{ if(!a || a->val==val) return a;
return recherche(val>a->val?a->fd:a->fg,val);
}
abr ajoute(abr a,int val)
{ if(!a) return f(val);
if(val>a->val) a->fd=ajoute(a->fd,val);
if(valval) a->fg=ajoute(a->fg,val);
return a;
}
void add1(abr a)
{ if(a) add1(a->fg),a->val++,add1(a->fd); }
abr copie(abr a)
{ return a?nouv(copie(a->fg),a->val,copie(a->fd)):0; }
abr oppose(abr a)
{ return a?nouv(oppose(a->fd),-a->val,oppose(a->fg)):0; }
int somme(abr a)
{ return a?somme(a->fg)+a->val+somme(a->fd):0; }
abr ote(abr a,int val)
{ if(!a) return 0;
if(val>a->val) return a->fd=ote(a->fd,val), a;
if(valval) return a->fg=ote(a->fg,val), a;
abr b;
if(!a->fg) return b=a->fd, free(a), b;
if(!a->fd) return b=a->fg, free(a), b;
for(b=a->fd;b->fg;b=b->fg) ;
return a->fd=ote(a->fd,a->val=b->val), a;
}
abr tourneg(abr a) /* a b */
{ abr b=a->fd; /* / \ / \ */
a->fd=b->fg; /* x b --> a z */
b->fg=a; /* / \ / \ */
return b; /* y z x y */
}
abr tourned(abr a) /* a --> b */
{ abr b=a->fg; /* / \ / \ */
a->fg=b->fd; /* b z x a */
b->fd=a; /* / \ / \ */
return b; /* x y y z */
}
abr linearise1(abr a)
{ if(!a) return a;
while(a->fd) a=tourneg(a);
a->fg=linearise1(a->fg);
return a;
}
void linearise2(abr*a)
{ if(!*a) return;
while((*a)->fd) *a=tourneg(*a);
linearise2(&(*a)->fg);
}
void linearise3(abr*a)
{ for(;*a;a=&(*a)->fg) while((*a)->fd) *a=tourneg(*a);
}
void affarb(abr a,int n)
{ if(a) affarb(a->fd,n+1), printf("%*s%d\n",3*n,"",a->val), affarb(a->fg,n+1);
}
void affaln(abr a) {printf("\n"), affarb(a,0);}
abr raccourci(abr a,int x) // réduit la longueur de la branche menant à x
{ if(!a || x==a->val) return a; // on est au bout de la branche
if( x> a->val) /* a --> b */
{ abr b=a->fd; /* \ / \ */
if(!b || x==b->val) return a; /* b a */
if( x> b->val) return b->fd=raccourci(b->fd,x), tourneg(a); /* \ */
{ abr c=b->fg;
if(!c || x==c->val) return a; /* a --> b */
if( x> c->val) return raccourci(tourneg(a),x); /* \ / */
return a->fd=raccourci(a->fd,x), a;/* b a -> a --> c */
} /* / \ \ / \ */
} /* c c c a */
{ abr b=a->fg; /* \ \ \ */
if(!b || x==b->val) return a;
if( x< b->val) return b->fg=raccourci(b->fg,x), tourned(a);
{ abr c=b->fd;
if(!c || x==c->val) return a;
if( x< c->val) return raccourci(tourned(a),x);
return a->fg=raccourci(a->fg,x), a;
}
}
}
abr reequilibre(abr a)
{ if(a->eq>0) if(a->fd->eq>=0) return a->eq=-(--a->fd->eq), tourneg(a);
else a->fd=tourned(a->fd), a=tourneg(a);
else if(a->fg->eq<=0) return a->eq=-(++a->fg->eq), tourned(a);
else a->fg=tourneg(a->fg), a=tourned(a);
a->fg->eq=a->fd->eq=0;
if(a->eq<0) a->fd->eq= 1;
if(a->eq>0) a->fg->eq=-1;
a->eq=0;
return a;
}
int haug; // booléen qui indique si la hauteur de l'arbre a varié lors de la dernière insertion
// ou suppresion dans un avl.
abr ajouteavl(abr a,int x)
{ if(!a) return haug=1, f(x); // L'arbre vide est remplacé par un nouveau noeud. Sa hauteur augmente donc
if(x==a->val) return haug=0, a; // x est déjà dans l'arbre (dans sa racine). On ne le modifie pas.
if(xval) a->fg=ajouteavl(a->fg,x), haug*=-1; // On ajoute x dans le sous-arbre gauche.
else a->fd=ajouteavl(a->fd,x) ; // On ajoute x dans le sous-arbre droit.
if(!haug) return a; // La hauteur des fils n'a pas changé.
if(haug!=a->eq) a->eq+=haug; // La hauteur d'un des fils a augmenté de 1. Donc a->eq varie de 1.
else a=reequilibre(a);// S'il passe à 2 ou -2, on fait des rotations.
return haug=!!a->eq, a; // Qu'il y ait eu ou non des rotations, la hauteur de l'arbre a augmenté
} // si et seulement s'il n'est pas bien tassé (a->eq non nul)
abr detacheavl(abr a,int x) // Fait la même chose que oteavl(a,x) sans appel à free
{ abr b;
if(!a) return haug=0, a; // L'arbre est vide, donc x n'est pas dedans, on ne le modifie pas
if(x>a->val) a->fd=detacheavl(a->fd,x), haug*=-1; else // x est dans le sous-arbre droit
if(xval) a->fg=detacheavl(a->fg,x) ; else // x est dans le sous-arbre gauche
{ if(!a->fg) return haug=1, a->fd; // x est dans la racine, qui n'a pas de fils gauche (droit)
if(!a->fd) return haug=1, a->fg; // On remplace donc l'arbre par l'autre fils.
for(b=a->fg;b->fd;b=b->fd) ; // b est l'élément le plus à droite du sous-arbre gauche
b->fg=detacheavl(a->fg,b->val); // On le détache du sous-arbre gauche, puis on le met
b->fd=a->fd; // à la place de a à la racine.
b->eq=a->eq; // Tout se passe alors, comme si on avait enlevé
a=b; // un élément du sous-arbre gauche.
}
if(!haug) return a; // La hauteur des fils n'a pas changé.
if(haug!=a->eq) a->eq+=haug; // La hauteur d'un des fils a diminué de 1. Donc a->eq varie de 1.
else a=reequilibre(a);// S'il passe à 2 ou -2, on fait des rotations.
return haug=!a->eq, a; // Qu'il y ait eu ou non des rotations, la hauteur de l'arbre a diminué
} // si et seulement s'il est bien tassé (a->eq nul)
abr oteavl(abr a,int x)
{ abr b=a;
while(b && b->val!=x) b=x>b->val?b->fd:b->fg;
if(b) a=detacheavl(a,x), free(b);
return a;
}
void verifavl(abr a)
{ abr x=0;
int hauteur(abr a)
{ if(!a) return 0;
int hg=hauteur(a->fg);
if(x && x->val>=a->val) printf(" Clefs non croissantes"),getchar(),exit(1);
x=a;
int hd=hauteur(a->fd);
if(hd-hg!=a->eq || a->eq<-1 || a->eq>1) printf(" erreur %d-%d=%d",hd,hg,a->eq),getchar(),exit(1);
return 1+(hgfg)
{ abr p=a->fg; a->fg=0, affar(a->fd,r), a->fg=p;//On cache le fils gauche pendant l'affichage du droit
int g=2;
for(p=r;p!=a;p=p->fg?:p->fd) printf(g!=!p->fg?" ":"| "), g=!!p->fg;
printf("%c--+(%2d) %d\n","/\\-"[g],a->eq,a->val);
}
}
void affarln(abr a) {printf("\n"),affar(a,a);}
int main()
{ abr a=nouv(f(1),4,nouv(f(7),8,0));
int j;
affln(a);
a=ajoute(a,6);
affln(a);
for(j=0;j<20;j+=2) if(recherche(a,j)) printf("%d ",j);
printf("\n");
abr b=copie(a);
add1(b);
printf("%d : ",somme(a)), affln(a);
printf("%d : ",somme(b)), affln(b);
abr c=oppose(b);
printf("%d : ",somme(c)), affln(c);
while(a) a=ote(a,a->val), affln(a);
affaln(b);
b=tourneg(b);
affaln(b);
b=tourned(b);
affaln(b);
//b=linearise1(b);
//linearise2(&b);
linearise3(&b);
affaln(b);
for(j=0;j<50000;++j) b=ajoute(b,j), b=raccourci(b,j);
//affln(b);
for(j=0;j<50000;++j) b=raccourci(b,j), b=ote(b,j);
affln(b);
for(j=0;j<50000;j++) b=ajouteavl(b,j^23521);
verifavl(b);
for(j=0;j<50000;j++) b=oteavl(b,j^23521);
verifavl(b);
affln(b);
return 0;
}
Le programme précédent écrit
1 4 7 8
1 4 6 7 8
4 6 8
26 : 1 4 6 7 8
31 : 2 5 7 8 9
-31 : -9 -8 -7 -5 -2
1 6 7 8
1 7 8
1 8
1
9
8
7
5
2
9
8
7
5
2
9
8
7
5
2
9
8
7
5
2
suivant précédent sommaire
lundi 3 et mardi 4 janvier 2005 : rotations dans les arbres binaires de recherche
Dans le programme précédent, tourneg, tourned, affarb, affaln, linearise1 ... ont été faits
le lundi 3 janvier 2005. Et raccourci a été fait le mardi 4 janvier 2005.
Rien n'a été fait sur les AVL. Dans la structure noeud il n'y avait donc pas le champ eq.
précédent sommaire
lundi 10 et mardi 11 janvier 2005 : AVL
lundi 10 janvier : rééquilibrage dans les AVL
On fait a=reequilibre(a) quand a est presque un AVL : C'est un arbre binaire de
recherche dans lequel les champs eq sont à jour. Dans chaque noeud eq est égal à la hauteur du sous-arbre
droit à laquelle on soustrait la hauteur du sous-arbre gauche. Tous les champs eq valent -1, 0 ou 1,
sauf pour la racine dont le champ eq vaut 2 ou -2.
On fait des rotations simples ou doubles pour faire disparaître ce 2 (ou ce -2).
Il y dix cas à considérer.
h+3 h+3 h+3 h+2
A devient B A devient B
/ 2 \h+2 h+2/-1 \ / 2 \h+2 h+1/ 0 \
h B A h+1 h B A h+1
/ 0 \ / 1 \ / 1 \ / 0 \
h+1 h+1 h h+1 h h+1 h h
Ces deux cas peuvent être regroupés, si on remarque que le B.eq diminue de 1 et que
la nouvelle valeur de A.eq est l'opposée de la nouvelle valeur de B.eq.
h+3 h+2 h+3 h+2 h+3 h+2
A devient C A devient C A devient C
/ 2 \h+2 h+1/ 0 \h+1 / 2 \h+2 h+1/ 0 \h+1 / 2 \h+2 h+1/ 0 \h+1
h B A B h B A B h B A B
/-1 \ / 0 \ / 0 \ /-1 \ /-1 \ / 0 \ /-1 \ / 0 \ / 1 \
h+1 h h h h h h+1 h h h-1 h h h+1 h h h h-1 h
C C C
/ 0 \ / 1 \ /-1 \
h h h-1 h h h-1
La rotation double se fait par une première rotation à droite, du fils droit de A, qui fait monter
C au dessus de B, suivie d'une autre rotation simple à gauche, de A qui fait monter C au dessus de A.
Après cela A.eq, B.eq et C.eq sont tous les trois mis à zéro, sauf A.eq qui est mis à -1 si C.eq valait 1
et B.eq qui est mis à 1 si C.eq valait -1.
On remarque que dans tous les cas sauf le premier, la hauteur diminue de 1 (elle passe de h+3 à h+2) et le
facteur d'équilibrage de la racine du nouvel arbre est 0.
Dans le premier cas la hauteur est inchangée (elle reste à h+3) et le facteur d'équilibrage final de la racine est non nul (il vaut -1).
On peut donc regarder la valeur finale du facteur d'équilibrage de la racine, pour savoir si la hauteur
a diminué de 1 ou pas.
Les cinq autres cas se présentent quand le facteur d'équilibrage initial de racine est -2.
mardi 11 janvier : insertion dans un AVL
a=ajouteavl(a,34) ajoute un nouveau noeud contenant 34 dans l'avl a.
Quand on insère un élément dans le sous-arbre droit et que sa hauteur augmente de 1, il y a quatre cas
à considérer :
h+1 h+1
A devient A
/-1 \ / 0 \
h h-1 h h
h+1 h+2
A devient A
/ 0 \ / 1 \
h h h h+1
h+1 h+2 h+2 h+1
A devient A qui devient B ou B
/ 1 \ / 2 \ -1 0
h-1 h h-1 h+1
A.eq augmente donc d'abord de 1.
Puis s'il est passé de 1 à 2, on appelle reequilibre.
Après tout ça, pour savoir si la hauteur de l'arbre a augmenté ou pas, il suffit de regarder si
le facteur d'équilibrage de la racine est non nul.
En effet sur les quatre cas, il y en a deux où la hauteur reste égale à h+1 avec un facteur d'équilibrage
de la racine nul, et deux cas où la hauteur passe de h+1 à h+2 avec un facteur d'équilibrage
de la racine qui vaut 1 ou -1.
Les fonctions detacheavl, oteavl, affar et affarln n'ont pas été vues.