Sommaire du Cours
- Complexite Algorithmique – Big-O, Omega, Theta
- Recursivite – Factorielle, Fibonacci, Tours de Hanoi
- Structures de Donnees – Tableaux, listes, piles, files, ABR, hachage
- Algorithmes de Tri – Bulle, insertion, fusion, rapide
- Tableaux & Chaines – Manipulation, pattern matching
- Exercices Corriges – Mise en pratique
- QCM – Teste tes connaissances
1. Complexite Algorithmique
La complexite algorithmique mesure les ressources (temps, memoire) consommees par un algorithme en fonction de la taille $n$ de l'entree. On utilise les notations asymptotiques pour decrire le comportement quand $n \to \infty$.
1.1 Notations Asymptotiques
| Notation | Nom | Definition intuitive | Exemple |
|---|---|---|---|
| $O(g(n))$ | Grand O (pire cas) | Borne superieure asymptotique. $f(n)$ ne croit pas plus vite que $g(n)$. | Tri bulle : $O(n^2)$ |
| $\Omega(g(n))$ | Grand Omega (meilleur cas) | Borne inferieure asymptotique. $f(n)$ croit au moins aussi vite que $g(n)$. | Tri insertion (deja trie) : $\Omega(n)$ |
| $\Theta(g(n))$ | Theta (cas moyen) | Borne exacte. $f(n)$ croit exactement comme $g(n)$. | Recherche dichotomique : $\Theta(\log n)$ |
1.2 Classes de Complexite – Echelle
| Classe | Notation | $n=100$ | $n=10^6$ | Exemple typique |
|---|---|---|---|---|
| Constante | $O(1)$ | 1 | 1 | Acces tableau par indice |
| Logarithmique | $O(\log n)$ | ~7 | ~20 | Recherche dichotomique |
| Lineaire | $O(n)$ | 100 | $10^6$ | Parcours de tableau |
| Linearithmique | $O(n \log n)$ | ~664 | ~$2 \times 10^7$ | Tri fusion, tri rapide |
| Quadratique | $O(n^2)$ | 10 000 | $10^{12}$ | Tri bulle, double boucle |
| Exponentielle | $O(2^n)$ | ~$10^{30}$ | infaisable | Fibonacci naif |
1.3 Regles de Calcul
Simplification du Grand O
- Constantes multiplicatives ignorees : $O(3n) = O(n)$, $O(n/2) = O(n)$
- Termes dominants seulement : $O(n^2 + n) = O(n^2)$, $O(2^n + n^{100}) = O(2^n)$
- Boucles imbriquees : $k$ boucles de taille $n \Rightarrow O(n^k)$
- Boucles successives : $O(f(n)) + O(g(n)) = O(\max(f,g))$
2. Recursivite
Une fonction recursive s'appelle elle-meme. Elle doit avoir un cas de base (condition d'arret) et un cas recursif (appel sur un sous-probleme plus petit).
2.1 Factorielle
def factorielle(n): # Cas de base if n <= 1: return 1 # Cas recursif return n * factorielle(n - 1)
Complexite : $O(n)$ – $n$ appels recursifs, $O(1)$ par appel.
2.2 Suite de Fibonacci
def fibonacci(n): if n <= 1: return n return fibonacci(n - 1) + fibonacci(n - 2)
Complexite naive : $O(2^n)$ – explosion exponentielle ! Avec memoisation : $O(n)$.
2.3 Tours de Hanoi
def hanoi(n, depart, arrivee, auxiliaire): if n == 1: print(f"Deplacer disque 1 de {depart} vers {arrivee}") return hanoi(n - 1, depart, auxiliaire, arrivee) print(f"Deplacer disque {n} de {depart} vers {arrivee}") hanoi(n - 1, auxiliaire, arrivee, depart)
Complexite : $O(2^n)$ – $2^n - 1$ deplacements. Pour $n=64$ disques, c'est environ 585 milliards d'annees !
3. Structures de Donnees
Une structure de donnees organise et stocke les donnees pour un acces et une modification efficaces. Le choix de la structure impacte directement la performance des algorithmes.
3.1 Comparatif des Structures Fondamentales
| Structure | Acces | Recherche | Insertion | Suppression |
|---|---|---|---|---|
| Tableau (Array) | $O(1)$ | $O(n)$ | $O(n)$ | $O(n)$ |
| Liste Chainee | $O(n)$ | $O(n)$ | $O(1)$ | $O(1)$ |
| Pile (Stack) | $O(n)$ | $O(n)$ | $O(1)$ | $O(1)$ |
| File (Queue) | $O(n)$ | $O(n)$ | $O(1)$ | $O(1)$ |
| ABR (BST) | $O(\log n)$ | $O(\log n)$ | $O(\log n)$ | $O(\log n)$ |
| Table de Hachage | – | $O(1)$ | $O(1)$ | $O(1)$ |
* Moyennes ; cas pire : ABR non equilibre → $O(n)$, table de hachage avec collisions → $O(n)$.
3.2 Pile et File – Principe LIFO / FIFO
Pile (Stack) – LIFO
Last In, First Out. Comme une pile d'assiettes. Operations : push() (empiler), pop() (depiler).
Utilisations : annulation (Ctrl+Z), parcours DFS, evaluation d'expressions, pile d'appels.
File (Queue) – FIFO
First In, First Out. Comme une file d'attente. Operations : enqueue() (enfiler), dequeue() (defiler).
Utilisations : parcours BFS, buffer de messages, files d'impression, ordonnancement.
3.3 Arbre Binaire de Recherche (ABR / BST)
Propriete fondamentale de l'ABR
Pour tout noeud $x$ : toutes les cles du sous-arbre gauche sont $\lt x$, et toutes les cles du sous-arbre droit sont $\gt x$.
Parcours infixe (gauche → racine → droite) donne les elements tries : 1, 3, 4, 6, 7, 8, 10, 13, 14.
Parcours prefixe (racine → gauche → droite) : 8, 3, 1, 6, 4, 7, 10, 14, 13.
Parcours suffixe (gauche → droite → racine) : 1, 4, 7, 6, 3, 13, 14, 10, 8.
3.4 Table de Hachage
Principe du Hachage
Une fonction de hachage $h(k)$ transforme une cle $k$ en un indice de tableau. Idealement $O(1)$ pour insertion/recherche/suppression.
Gestion des collisions :
- Chainage : chaque case contient une liste chainee des elements en collision
- Adressage ouvert : sondage lineaire, quadratique ou double hachage pour trouver une case libre
En Python : dict, en Java : HashMap. Facteur de charge $\alpha = n/m$ (n cles, m cases) – si $\alpha > 0.7$, redimensionner.
4. Algorithmes de Tri
Trier est l'une des operations les plus fondamentales en informatique. Voici les 4 algorithmes incontournables, du plus simple au plus efficace.
Tri a Bulles (Bubble Sort)
Compare et echange les elements adjacents. Les plus grands "remontent" comme des bulles.
Meilleur : $O(n)$ | Moyen : $O(n^2)$ | Pire : $O(n^2)$
Stable : Oui | En place : Oui | Utilise : Rarement (pedagogique)
Tri par Insertion
Insere chaque element a sa place dans la partie deja triee. Efficace sur petites donnees ou quasi-triees.
Meilleur : $O(n)$ | Moyen : $O(n^2)$ | Pire : $O(n^2)$
Stable : Oui | En place : Oui | Utilise : Petits tableaux, TimSort
Tri Fusion (Merge Sort)
Divise recursivement en deux moities, trie chacune, puis fusionne. Paradigme "diviser pour regner".
Meilleur/Moyen/Pire : $O(n \log n)$
Stable : Oui | En place : Non ($O(n)$ aux.) | Utilise : Donnees volumineuses, externe
Tri Rapide (Quick Sort)
Choisit un pivot, partitionne (elements ≤ pivot a gauche, ≥ a droite), trie recursivement. Le plus utilise en pratique.
Meilleur/Moyen : $O(n \log n)$ | Pire : $O(n^2)$
Stable : Non | En place : Oui | Utilise : Tri standard dans la plupart des langages
4.1 Tableau Comparatif des Tris
| Algorithme | Meilleur cas | Cas moyen | Pire cas | Memoire | Stable |
|---|---|---|---|---|---|
| Tri Bulle | $O(n)$ | $O(n^2)$ | $O(n^2)$ | $O(1)$ | Oui |
| Tri Insertion | $O(n)$ | $O(n^2)$ | $O(n^2)$ | $O(1)$ | Oui |
| Tri Selection | $O(n^2)$ | $O(n^2)$ | $O(n^2)$ | $O(1)$ | Non |
| Tri Fusion | $O(n \log n)$ | $O(n \log n)$ | $O(n \log n)$ | $O(n)$ | Oui |
| Tri Rapide | $O(n \log n)$ | $O(n \log n)$ | $O(n^2)$ | $O(\log n)$ | Non |
5. Tableaux et Chaines de Caracteres
Manipuler efficacement tableaux et chaines est essentiel. Voici les techniques classiques et la recherche de motifs.
5.1 Techniques Classiques sur Tableaux
Technique des Deux Pointeurs
Un pointeur a gauche, un a droite. Se rapprochent tant que condition satisfaite. Ideal pour tableaux tries.
Complexite : $O(n)$. Exemples : inversion de tableau, somme de deux elements = cible, verification palindrome.
Fenetre Glissante (Sliding Window)
Maintenir une fenetre de taille fixe ou variable qui "glisse" sur le tableau. Optimise les sous-tableaux.
Complexite : $O(n)$. Exemples : somme maximale de k elements consecutifs, plus longue sous-chaine sans repetition.
5.2 Recherche de Motifs (Pattern Matching)
Algorithmes de recherche de sous-chaine
| Algorithme | Complexite | Principe |
|---|---|---|
| Naif | $O(n \cdot m)$ | Compare le motif a chaque position |
| KMP (Knuth-Morris-Pratt) | $O(n + m)$ | Precalcule un tableau de prefixes pour eviter les retours |
| Boyer-Moore | $O(n)$ (meilleur) | Compare de droite a gauche, saute des blocs entiers |
| Rabin-Karp | $O(n + m)$ (moyen) | Utilise une fonction de hachage glissante |
$n$ = longueur du texte, $m$ = longueur du motif.
6. Exercices Corriges
Mets en pratique les concepts avec ces 4 exercices, du niveau debutant a avance.
Analysez la complexite temporelle (pire cas) du code suivant :
def mystere(arr, n): for i in range(n): j = 1 while j < n: print(arr[i] * j) j = j * 2
Complexite : $O(n \log n)$
La boucle for s'execute $n$ fois. La boucle while double j a chaque iteration jusqu'a $n$, soit $\log_2(n)$ iterations. Total : $n \times \log n = O(n \log n)$.
Ecrivez une fonction recursive somme_chiffres(n) qui retourne la somme des chiffres d'un entier positif. Exemple : somme_chiffres(12345) → 15.
def somme_chiffres(n): # Cas de base : un seul chiffre if n < 10: return n # Dernier chiffre + appel sur le reste return (n % 10) + somme_chiffres(n // 10)
Complexite : $O(\log_{10} n) = O(d)$ ou $d$ est le nombre de chiffres.
On insere successivement les valeurs suivantes dans un ABR initialement vide : 15, 10, 20, 8, 12, 17, 25, 6, 11, 16, 19.
(a) Dessinez l'ABR obtenu.
(b) Quelle est la hauteur de l'arbre ?
(c) Combien de comparaisons pour trouver 16 ?
(b) Hauteur = 3 (15 → 10 → 8 → 6).
(c) 16 : 15→20→17→16 = 4 comparaisons. Chemin : 15 (aller a droite), 20 (gauche), 17 (gauche), 16 (trouve !).
On dispose d'un tableau de $10^6$ entiers. Pour chaque scenario, quel algorithme de tri recommandez-vous ? Justifiez.
(a) Le tableau est deja presque trie (moins de 10 elements mal places).
(b) La memoire disponible est tres limitee.
(c) La stabilite est requise (elements egaux conservent leur ordre relatif).
(a) Tri par insertion – excellent sur donnees quasi-triees : $\approx O(n)$ avec tres peu d'echanges.
(b) Tri rapide – tri en place avec $O(\log n)$ de memoire pour la pile de recursion. Le tri fusion necessiterait $O(n)$ memoire auxiliaire.
(c) Tri fusion – stable par conception (fusion preserve l'ordre). Tri rapide n'est pas stable (partitionnement melange).
QCM – Teste tes Connaissances
5 questions pour evaluer ta maitrise du cours de programmation et algorithmique. Propulse par le moteur QCM de NESTI STUDIO.