Académique Documents
Professionnel Documents
Culture Documents
1.1 Variables
Les variables sont des désignateurs qui représentent simplement des emplacements
de stockage dans la mémoire principale utilisés pour stocker des données. De
l’espace est alloué en mémoire pour chaque variable déclarée dans le programme. La
taille de cet espace dépend du type de la variable. Par exemple, 2 octets sont alloués
pour le type integer, 4 octets sont alloués pour le type float, etc.
Programme 1.1
1 #include <stdio.h>
2
3 int main()
4 {
5 int var1,var2,var3;
6 var1=100;
7 var2=200;
8 var3=var1+var2;
9 printf("Additionner %d et %d donnera %d", var1,var2,var3);
10 return 0;
11 }
Analyse du programme
Ligne 5: La mémoire est allouée pour les variables var1, var2 et var3. Chaque fois
que nous déclarons une variable, de la mémoire est allouée pour stocker la valeur
affectée à la variable. Dans notre exemple, 2 octets sont alloués pour chacune des
variables qui sont de type int.
Lignes 6 et 7: La valeur 100 est affectée dans la variable var1 et la valeur 200 est
affectée à la variable var2.
Ligne 8: Les valeurs de var1 et var2 sont additionnées et le résultat est affecté à la
variable var3.
Ligne 9: Enfin, les trois valeurs de var1, var2 et var3 sont affichées à l'écran.
1.2 Pointeurs
Les pointeurs ne sont rien de plus que des variables qui stockent les adresses en
mémoire d'autres variables et peuvent être utilisés pour accéder à la valeur stockée à
ces adresses. Divers opérateurs tels que *, & et [] nous permettent d'utiliser des
pointeurs.
Programme 1.2
1 int main()
2 {
3 int var;
4 int* ptr;
5 var = 10;
6 ptr = &var;
7
8 printf("Valeur stockée pour la variable var est %d\n",var);
9 printf("Valeur stockée pour la variable var est %d\n", *ptr);
10
11 printf("L’adresse de la variable var est %p \n", &var);
12 printf("L’adresse de la variable var est %p \n", ptr);
13 return 0;
1
Obtenir à partir d'un pointeur l'adresse d'un élément de données qui se trouve dans un autre emplacement.
©Emmanuel Chimi, 03/2023 | Algorithmes et structures de données 4
14 }
Analyse du programme
Ligne 3: Une variable de type integer var est déclarée.
Ligne 4: Un pointeur de nom ptr sur une variable de type int est déclaré.
Ligne 6: L'adresse de var est stockée à l’emplacement sur lequel pointe ptr.
Lignes 8 et 9: La valeur stockée dans la variable var est affichée à l'écran.
L'opérateur * est utilisé pour obtenir la valeur stockée à l'emplacement du pointeur.
Lignes 11 & 12: L'adresse mémoire de var est affichée à l'écran. L'opérateur
d'adresse & est aussi utilisé pour obtenir l'adresse d'une variable.
Il faut constater que "é" ne fait pas partie du jeu de caractères utilisé par le compilateur, d'où
l'affichage "stockÚe".
Points à retenir
• On déclare un pointeur en incluant un * devant le nom de la variable qui doit être
un pointeur. (Déclaration de pointeur)
• On peut obtenir la valeur stockée à l'adresse en ajoutant * avant le nom de du
pointeur. (Utilisation du pointeur)
• On peut obtenir l'adresse d'une variable en utilisant l'opérateur &.
1.3 Tableaux
Un tableau est une structure de données utilisée pour stocker plusieurs éléments de
données du même type. Toutes les données sont stockées séquentiellement, c'est-à-
dire sur des emplacements consécutifs en mémoire. La valeur stockée à n'importe
quel index est accessible directement et en temps constant. Les tableaux font partie
des structures de données statiques.
Analyse du programme
Ligne 3: Déclare un tableau d'entiers de nom tab. Le tableau est de taille 10, ce qui
signifie qu'il peut stocker 10 entiers, les indices allant de 0 à 9.
Ligne 6: Les éléments du tableau sont accessibles à l'aide de l'opérateur d'indice [].
L'indice le plus bas est 0 et l'indice le plus élevé est (taille du tableau − 1). Les
valeurs 0 à 9 sont stockées dans le tableau aux indices respectifs de 0 à 9.
Ligne 8: Le tableau et sa taille sont passés à la fonction printArray().
Il faut remarquer que le programme 1.3 n'est pas complet parce qu'il utilise une fonction qui
n'est ni une fonction importée ni implémentée dans le programme directement.
Programme 1.4
1 void printArray(int arr[], int count)
2 {
3 printf("Valeurs stockées dans le tableau sont : ");
4 for (int i = 0; i < count; i++)
5 {
6 printf(" [ %d ] ", arr[i]);
7 }
8 }
Analyse du programme
Ligne 1: La variable de tableau arr et son nombre d'éléments sont passés comme
arguments à la fonction printArray().
Ligne 4-7: Enfin, les valeurs du tableau sont affichées à l'écran à l'aide de la fonction
printf dans une boucle.
Point à retenir:
1. L'indice de tableau commence toujours à partir de l'indice 0 et l'indice le plus
élevé est de (taille du tableau) − 1.
2. L'opérateur d'indice a la priorité la plus élevée si on écrit arr[2]++. Ensuite, la
valeur de arr[2] sera incrémentée.
3. Chaque élément du tableau a une adresse mémoire.
Programme 1.5
1 void printArrayAddress(int arr[], int count)
2 {
3 printf("Les valeurs enregistrées sont : ");
4 for (int i = 0; i < count; i++)
5 {
6 printf("La valeur: [%d] a pour adresse: [%p] \n", arr[i], arr+ i);
7 }
8 }
Analyse du programme
Ligne 6: La valeur stockée dans un tableau est affichée. L'adresse des différents
éléments du tableau est également affichée.
Programme 1.6
1 void printArrayUsingPointer(int arr[], int count)
2 {
3 printf("Les valeurs stockées sont : ");
4 int* ptr = arr;
5 for (int i = 0; i < count; i++)
6 {
7 printf("La valeur: [%d] a pour adresse: [%p] \n", *ptr, ptr);
8 ptr++;
9 }
10 }
Analyse du programme
Ligne 4: Un pointeur sur une variable de type int est déclaré et il pointera sur le
tableau (premier élément).
Ligne 7: La valeur stockée dans le pointeur est affichée à l'écran.
Ligne 8: Le pointeur est incrémenté.
Programme 1.7
1 int main()
2 {
3 int arr[4][2];
4 int count = 0;
5 for (int i = 0; i < 4; i++)
6 for (int j = 0; j < 2; j++)
7 arr[i][j] = count++;
8
9 print2DArray((int**)arr, 4, 2);
10 print2DArrayAddress((int**)arr, 4, 2);
11 }
Analyse du programme
• Le corps du programme commence par la déclaration d'un tableau de dimension
4 x 2. Le tableau aura 4 lignes et 2 colonnes.
• Les valeurs sont enregistrées dans le tableau dans une boucle imbriquée.
Programme 1.8
1 void printArray(int* arr[], int count)
2 {
3 int *ptr;
4 for (int i = 0; i < count; i++)
5 {
6 ptr = arr[i];
7 printf("[ %d ]", *ptr);
8 }
9 }
1 int main()
2 {
3 int one = 1, two = 2, three = 3;
4 int* arr[3];
5 arr[0] = &one;
6 arr[1] = &two;
7 arr[2] = &three;
8 printArray(arr, 3);
9 printArrayAddress(arr, 3);
10 }
Programme 1.9
int SumArray(int arr[], int size)
{
int total=0;
int index=0;
for(index=0;index<size;index++)
{
total = total + arr[index];
}
return total;
}
Programme 1.10
int SequentialSearch(int arr[], int size, int value)
{
int i = 0;
for(i = 0; i < size; i++)
{
if(value == arr[i] )
return i;
}
return -1;
}
• Puisque nous n'avons aucune idée des données stockées dans le tableau ou si les
données ne sont pas triées, nous devons rechercher sur les éléments du tableau
de manière séquentielle, un par un.
• Si nous trouvons la valeur que nous recherchons la fonction renvoie l’index
correspondant.
Dans l'exemple ci-dessus, les données ne sont pas triées. Si les données sont triées,
une recherche binaire peut être effectuée. Nous examinons la position médiane à
chaque étape. Selon la clé que nous recherchons, elle est supérieure ou inférieure à
la valeur médiane. Nous continuons la recherche soit dans la partie gauche, soit dans
la partie droite du tableau. A chaque étape, on y élimine la moitié de l'espace de
recherche, ce qui rend cet algorithme très efficace.
Programme 1.11
/* Binary Search Algorithm – Iterative Way */
int BinarySearch (int arr[], int size, int value)
{
int low = 0, mid;
int high = size-1;
while (low <= high)
{
mid = low + (high-low)/2; /* To avoid the overflow */
if (arr[mid] == value)
return mid
else
Analyse du programme
• Puisque nous avons des données triées par ordre croissant/décroissant, nous
pouvons appliquer une recherche binaire plus efficace. A chaque étape, nous
réduisons de moitié notre espace de recherche.
• A chaque étape, nous comparons la valeur médiane avec la valeur que nous
recherchons. Si la valeur médiane est égale à la valeur que nous recherchons,
nous renvoyons l'index du milieu.
• Si la valeur est inférieure à la valeur médiane, nous recherchons dans la moitié
gauche du tableau.
• Si la valeur est supérieure à la valeur médiane, nous recherchons dans la moitié
droite du tableau.
• Dans l’ensemble, si nous trouvons la valeur que nous recherchons, son index est
renvoyé ou, sinon, -1 est renvoyé par la fonction.
Programme 1.12
void rotateArray(int *a,int n,int k)
{
reverseArray(a,k);
reverseArray(&a[k],n-k);
reverseArray(a,n);
}
void reverseArray(int *a,int n)
{
for(int i=0,j=n-1;i<j;i++,j--)
{
a[i]^=a[j]^=a[i]^=a[j];
}
}
Analyse du programme
• La rotation du tableau se fait en deux parties. Dans la première partie, nous
inversons d'abord les éléments de la première moitié du tableau, puis seconde
moitié.
• Ensuite, nous inversons tout le tableau en complétant toute la rotation.
Exercice 5:
Détermination de la plus grande somme de sous-tableau contigu.
Étant donné un tableau d'entiers positifs et négatifs, il faut trouver un sous-tableau
contigu dont la somme (somme des éléments) est maximisée.
Programme 1.13
int maxSubArraySum(int a[], int size)
{
int maxSoFar = 0, maxEndingHere = 0;
for (int i = 0; i < size; i++)
{
maxEndingHere = maxEndingHere + a[i];
if (maxEndingHere < 0)
maxEndingHere = 0;
if (maxSoFar < maxEndingHere)
maxSoFar = maxEndingHere;
}
return maxSoFar;
}
Programme 1.14
1 struct coord {
2 int x;
3 int y;
4 };
5 int main()
6 {
7 struct coord point;
8 point.x=10;
9 point.y=10;
10 printf("Valeur de la coordonnée de l’axe des x est %d \n", point.x);
11 printf("Valeur de la coordonnée de l’axe des y est %d \n", point.y);
12 printf("Taille de la structure est de %d bytes\n", sizeof(point));
13 return 0;
14 }
Affichage du programme
Analyse du programme
Ligne 1 - 4: Nous avons déclaré la structure de nom coord, qui regroupe deux
éléments en son sein. Les deux éléments x et y correspondant aux coordonnées de
l'axe des x et de l'axe des y.
Ligne 7: Nous avons déclaré une variable de nom point comme étant de type
struct coord.
Ligne 8 - 9: Nous avons attribué des coordonnées (10, 10), aux composants x et y
de la variable point. Les différents éléments d'une structure sont accessibles à l'aide
de l'opérateur DOT (.).
Programme 1.15
1 #include<stdio.h>
2 struct student {
3 int rollNo;
4 char* firstName;
5 char* lastName;
6 };
7 int main()
8 {
9 int i=0;
10 struct student stud;
11 struct student* ptrStud;
12 ptrStud= &stud;
13 ptrStud->rollNo=1;
14 ptrStud->firstName ="john";
15 ptrStud->lastName ="smith";
16 printf("Roll No: %d Student Name: %s %s ", ptrStud->rollNo,
ptrStud->firstName, ptrStud->lastName);
17
18 return 0;
19 }
Analyse du programme
Ligne 2 - 6: Nous avons déclaré une structure student (étudiant) contenant le
numéro de rôle, le prénom et le nom d'un étudiant.
Ligne 12: Nous avons déclaré un pointeur sur la structure student.
Ligne 16 : Nous avons enfin affiché tous les différents éléments de la variable de la
structure stud.
Remarque: De la même manière, on peut utiliser l'opérateur -> pour accéder aux
éléments de l'Union.
Fonction malloc
La définition de la fonction malloc est la suivante :
void *malloc (size_t size);
La fonction alloue un bloc mémoire de taille size octets et renvoie un pointeur vers
le bloc. Elle retourne NULL si le système n'a pas assez de mémoire ou si l’opération
d’allocation a échoué pour une autre raison.
La norme C définit void* comme un pointeur générique qui doit être casté dans le
type requis. La plupart des compilateurs C ont besoin de ce casting. Cependant, la
dernière norme ANSI C ne l'exige pas. Par exemple.
int* p = (int *) malloc (sizeof(int));
Fonction calloc
La définition de la fonction calloc est la suivante.
void *calloc (size_t num, size_t size);
Elle alloue un bloc mémoire de taille num x size octets et renvoie un pointeur sur le
bloc. La fonction retourne NULL si le système n'a pas assez de mémoire. Une chose
supplémentaire qu’elle fait c’est qu’elle initialise chaque octet à zéro.
Cette fonction est utilisée pour modifier la taille du bloc de mémoire d'un bloc de
mémoire précédemment alloué et sur lequel pointe ptr. Elle renvoie un bloc mémoire
de taille newSize. Si la taille de bloc est augmentée, alors le contenu de l'ancien
bloc de mémoire est copié dans une région nouvellement allouée. Si le pointeur
renvoyé par la fonction est différent de l'ancien pointeur ptr, alors ptr ne pointera
plus sur un emplacement valide. Donc, en général, on ne doit pas utiliser ptr une fois
qu'il est passé à la fonction realloc() sans lui avoir attribué le nouveau pointeur
renvoyé. Si ptr est NULL, realloc fonctionne comme malloc().
Fonction free
La mémoire allouée à l'aide de malloc/calloc/realloc doit être libérée à l'aide d'une
fonction free(). La syntaxe de la fonction free() est la suivante.
void free(void *pointer);
1.9 Fonctions
Les fonctions dans le langage C et bien d’autres sont des sous-programmes tels que
introduits par la programmation structurée. Elles sont utilisées pour apporter de la
modularité au programme. En utilisant la fonction, nous pouvons diviser des tâches
complexes en tâches gérables plus petites. L'utilisation des fonctions permet
également d'éviter le code en double. Par exemple, nous pouvons définir une
fonction sum() qui prend deux entiers et renvoie leur somme. Ensuite, nous
pouvons utiliser cette fonction plusieurs fois chaque fois que nous voulons la somme
de deux entiers. Le programme suivant illustre l’appel d’une fonction.
Programme 1.16
1 #include <stdio.h>
2 /* Déclaration de la fonction */
3 int sum(int num1, int num2);
4 int main()
5 {
6 /* Déclaration de variables locales */
Affichage :
La somme est de: 30
Analyse du programme
Ligne 3: Déclaration de la fonction sum().
Ligne 11: La fonction sum est appelée à partir de main (Programme principal) en lui
passant les variables x et y avec les valeurs 10 et 20. A ce point le flux de contrôle
ira à la ligne 16.
Ligne 16: Les variables passées à la fonction sum sont copiées dans les variables
locales num1 et num2.
Ligne 20 & 21: La somme est calculée et enregistrée dans la variable result1. Et le
résultat est renvoyé. Le flux de contrôle revient à la ligne numéro 11.
Ligne 11-12: La valeur de retour de la fonction somme est enregistrée dans une
variable locale result et affichée à l'écran.
Figure 9
1. Initialement, la pile était vide. Ensuite, nous avons ajouté la valeur 1 à la pile en
utilisant l'opérateur push(1).
2. De même, push(2) et push(3)
3. L'opération pop prend le haut de la pile. Les données de la pile sont ajoutées et
supprimées de la manière "dernier entré, premier sorti".
4. La première opération pop() va retirer 3 de la pile.
5. De même, une autre opération pop prendra 2 puis 1 de la pile.
6. Au final, la pile est vide lorsque tous les éléments sont sortis de la pile.
Pour obtenir le point exact auquel l'exécution doit reprendre, l'adresse de l'instruction
suivante est stockée dans la pile. Lorsque l'appel de la fonction est terminé, l'adresse
en haut de la pile est supprimée.
Affichage
main line 1
fun1 line 1
fun2 line 1
fun1 line 2
main line 2
Analyse du programme
Ligne 13: Chaque programme commence par la fonction main().
Ligne 15: C'est la première instruction qui sera exécutée. Et nous obtiendrons la
"main line 1" en sortie.
Ligne 16: fun1() est appelée. Avant que le contrôle ne passe à fun1(), l'instruction
suivante qui est l'adresse de la ligne 17 est stockée dans la pile système.
Ligne 6: Le contrôle passe à la fonction fun1().
Ligne 8: C'est la première déclaration à l'intérieur de fun1(), qui va afficher "fun1
ligne 1" en sortie.
Points à retenir:
1. Les fonctions sont implémentées à l'aide d'une pile.
2. Lorsqu'une fonction est appelée, l'adresse de l'instruction suivante est poussée
(pushed) dans la pile.
3. Lorsqu'une fonction est terminée, l'adresse de l'exécution est retirée (poped) de
la pile.
Programme 1.18
1 void increment(int var)
2 {
3 var++;
4 }
5
6 int main()
7 {
8 int i = 10;
9 printf("Valeur de i avant incrémentation est : %d \n", i);
10 increment(i);
11 printf("Valeur de i après incrémentation est : %d \n", i);
12 }
Affichage
Valeur de i avant incrémentation est : 10
Valeur de i après incrémentation est : 10
Analyse du programme
Ligne 8: La variable "i" est déclarée et initialisée avec la valeur 10.
Ligne 9: Valeur de "i" est affichée.
Ligne 10: La fonction d'incrémentation est appelée. Lorsqu'une fonction est appelée
par valeur, la valeur de l'argument est copiée dans une autre variable de la fonction
appelée. Le flux de contrôle va à la ligne n° 1.
Ligne 3: La valeur de var est incrémentée de 1. Cependant, il faut se rappeler qu'il
ne s'agit que d'une copie à l'intérieur de la fonction d'incrémentation.
Ligne 11: Lorsque la fonction se termine, la valeur de "i" est toujours 10.
Programme 1.19
1 void increment(int *ptr)
2 {
3 (*ptr)++;
4 }
5 int main()
6 {
7 int i = 10;
8 printf("Valeur de i avant incrémentation est : %d \n", i);
9 increment(&i);
10 printf("Valeur de i après incrémentation est : %d \n", i);
11 }
Affichage
Valeur de i avant incrémentation est : 10
Valeur de i après incrémentation est : 11
Analyse du programme
Ligne 9: L'adresse de "i" est transmise à la fonction increment. Celle-ci prend, selon
la déclaration, un pointeur à int comme argument.
Ligne 3: On accède à la variable à l'adresse ptr et sa valeur est incrémentée.
Ligne 10: Enfin, la valeur incrémentée est affichée à l'écran.
Points à retenir:
L'appel par référence est implémenté indirectement en passant l'adresse d'une
variable à la fonction.
Remarque: La vitesse d'exécution d'un programme récursif est plus fiable en raison
des surcharges de la pile. Si la même tâche peut être effectuée en utilisant une
solution itérative (boucles), nous devrions préférer une solution itérative (boucles) à
la place de la récursivité pour éviter la surcharge de la pile.
Analyse du programme
Chaque fois que la fonction factorial appelle factorial c'est avec une valeur réduite
de l'argument, i - 1. La complexité temporelle est O(N).
Analyse du programme
Ligne 3: Chaque reste de la division est calculé et stocké comme chiffre dans son
équivalent digit de type char.
Ligne 4-5: Si le nombre est supérieur à 10, le nombre divisé par 10 est transmis à la
fonction printInt().
Ligne 6: Le nombre sera affiché avec le plus grand ordre d'abord, puis les chiffres
d'ordre inférieur.
La complexité temporelle est O(N).
Analyse du programme
Ligne 1: La valeur de base est également fournie avec le nombre.
Ligne 4: Le reste du nombre est calculé et stocké en chiffres.
Ligne 5-6: Si le nombre est supérieur à la base, alors le nombre divisé par la base est
passé en argument à la fonction printInt() de manière récursive.
Ligne 7: Le nombre sera affiché avec le plus grand ordre d'abord, puis les chiffres
d'ordre inférieur.
La complexité temporelle est O(N).
Analyse du programme
Ligne 1: Le tampon de caractères p est passé en argument et le nombre qui doit être
converti en chaîne est aussi passé comme argument.
Ligne 3: Le chiffre le moins significatif du nombre est converti en caractère
correspondant.
Ligne 4-5: Si le nombre est supérieur à 10, le nombre divisé par 10 est passé en
argument à la fonction intToStr() de manière récursive.
Ligne 6: Le caractère est stocké dans le tampon p d'ordre supérieur en premier, puis
les chiffres d'ordre inférieur.
La complexité temporelle est O(N).
Tour de Hanoi
La Tour de Hanoï (appelée aussi la Tour de Brahma). On nous donne trois tiges
et un nombre N de disques, initialement tous les disques sont empilés autour de la
première tige (la plus à gauche) par ordre décroissant de taille. L'objectif est de
transférer toute la pile de disques de la première tour à la troisième tour (la plus à
droite), en déplaçant un seul disque à la fois et jamais un plus grand sur un plus
petit. Il est permis de se servir de la tige intermédiaire pour un stockage provisoire,
tout en respectant l'interdiction d'avoir un disque sur un autre plus petit.
Programme 1.24
1 void towerOfHanoi(int num, char src, char dst, char temp)
2 {
3 if (num < 1)
4 return;
5
6 towerOfHanoi(num - 1, src, temp, dst);
7 printf("\n Move disk %d from peg %c to peg %c", num, src, dst);
Analyse du programme
Problème de TowerOfHanoi. Si nous voulons déplacer N disques de la source vers la
destination, nous déplaçons d'abord N-1 disques de la source vers la tige
intermédiaire (temp), puis déplaçons le Nième disque le plus bas de la source vers la
destination. Ensuite, les disques N-1 seront déplacés de temp vers la destination.
Analyse du programme
L'algorithme d'Euclide est utilisé pour trouver pgcd :
PGCD(n, m) == PGCD(m, n mod m)
Nombres de Fibonacci
Programme 1.26
1 int fibonacci(int n)
2 {
3 if (n <= 1)
4 return n;
5 return fibonacci(n - 1) + fibonacci(n - 2);
6 }
Analyse du programme
Les nombres de Fibonacci sont calculés en faisant la somme des deux nombres
précédents pour créer le prochain nombre. Il y a une inefficacité dans la solution,
nous chercherons une meilleure solution dans les prochains chapitres.
Analyse du programme
Dans la fonction de permutation, à chaque appel récursif le nombre à l'index "i" est
permuté avec tous les numéros qui sont à sa droite. Puisque le nombre est échangé
avec tous les nombres à sa droite un par un, il produira toutes les permutations
possibles.
Analyse du programme
La solution itérative au problème de recherche binaire a été déjà vue. Examinons
maintenant la solution récursive du même problème dans cette solution également,
nous plongeons l'espace de recherche en deux et faisons la même chose que nous
avions faite dans la solution itérative.
2. Écrire un programme qui trouve la somme de tous les éléments d'un tableau à
deux dimensions.
6. Écrire un programme qui affiche tous les maxima d'un tableau. (Une valeur est un
maximum si la valeur avant et après son index est plus petite qu'elle ne l'est ou
n'existe pas.)
Conseil :
• Commencez à parcourir le tableau à partir de la fin et gardez une trace de
l'élément max.
• Si nous rencontrons un élément > max, on affiche l'élément et on met à jour
max.
8. Étant donné une liste d'intervalles, écrire un programme qui fusionne tous les
intervalles qui se chevauchent. Par exemple:
Entrée: {[1, 4], [3, 6], [8, 10]}
Sortie: {[1, 6], [8, 10]}
9. Écrire une fonction qui prend des intervalles en entrée et effectue la fusion des
intervalles qui se chevauchent.
10. Écrire un programme qui inverse un tableau sur place. (On n’utilise pas un
tableau supplémentaire, ce qui va conduire à une complexité spatiale de O (1).)
Astuce: Utiliser deux variables, début et fin. Début est initialisé à 0 tandis que
fin est initialisé à (n-1). Incrémenter début et décrémenter fin. Permuter les
valeurs stockées à arr[début] et arr[fin]. On arrête le processus lorsque début
est égal fin ou lorsque début est supérieur fin.
11. Écrire un programme qui trie un tableau ne contenant que les valeurs 0 et 1 de
sorte que tous les 0 viennent avant tous les 1.
Astuce: Utiliser deux variables, début et fin. Début est initialisé à 0 tandis que
fin est initialisé à (n-1). Incrémenter début et décrémenter fin. Permuter les
valeurs stockées à arr[début] et arr[fin] si et seulement si arr[début]==1 et
12. On donne un tableau qui ne contient que les valeurs 0, 1 et 2. Écrire un code qui
trie le tableau de sorte que tous les 0 soient avant tous les 1 et tous les 1 avant
les 2.
Astuce: Comme dans un exercice précédent avec un tableau contenant les deux
valeurs 0 et 1 uniquement, il faut penser d'abord aux 0 et aux 1 comme un seul
groupe et déplacer tous les 2 sur le côté droit. On fait ensuite un deuxième
passage sur le tableau pour trier les 0 et les 1.
13. Écrire un code qui retrouve les éléments en double dans un tableau de taille n où
la valeur de chaque élément est comprise entre 0 et n-1.
Astuce:
Approche 1: On compare chaque élément avec tous les éléments du tableau (en
utilisant deux boucles). La complexité de cette solution est de O(n2)
Approche 2: On maintient une table de hachage. On fixe la valeur de hachage à 1
si nous rencontrons l'élément pour la première fois. Lorsque nous avons à
nouveau la même valeur, nous pouvons voir que la valeur de hachage est déjà 1,
nous pouvons donc afficher cette valeur comme étant un doublon. La complexité
de cette solution est réduite à O(n), mais elle requiert de l’espace
supplémentaire.
Approche 3: Nous allons exploiter la contrainte "La valeur de tout élément est
comprise entre 0 et n-1". On peut prendre un tableau arr[] de taille n et définir
tous les éléments sur 0. Chaque fois que nous obtenons une valeur, par exemple
val1. On va incrémenter la valeur à l'index arr[val1] de 1. À la fin, on peut
parcourir le tableau arr et afficher les valeurs répétées (arr[] > 0). La complexité
spatiale supplémentaire sera de O(n), ce qui sera inférieure à la même complexité
dans l'approche de la table de hachage.
14. Écrire une fonction qui permet de trouver la somme des chiffres da chaque
nombre de type entier. Par exemple: Pour le nombre 1984 donné en entrée, la
fonction renvoie 32 (1+9+8+4).