Skip to content

Introduction à la pensée algorithmique

Préface

Comment résoudre efficacement les problèmes ? Vous avez peut-être déjà rencontré cette situation : pour un même problème, le code de l'un donne un résultat en quelques secondes, tandis que celui d'un autre tourne toujours au bout de plusieurs minutes. La différence réside souvent dans l'algorithme. Ce chapitre vous guide vers la compréhension des concepts fondamentaux de la pensée algorithmique.

Que allez-vous apprendre dans cet article ?

À la fin de ce chapitre, vous aurez acquis :

  • Capacité de décomposition : face à des problèmes complexes, savoir utiliser des stratégies comme diviser pour régner ou la récursivité, plutôt que de coder immédiatement
  • Jugement d'efficacité : utiliser la notation grand O pour déterminer quelle solution est la plus efficace, plutôt que de deviner au feeling
  • Pensée en complexité : estimer la taille des données et les exigences temporelles avant de coder, pour choisir le bon niveau d'algorithme
  • Fondements pour la suite : préparer le terrain pour les structures de données avancées, les systèmes distribués et l'apprentissage automatique
ChapitreContenuConcepts clés
Chapitre 1Recherche binaireDiviser pour régner, O(log n)
Chapitre 2Algorithmes de triTri à bulles, tri rapide, tri fusion
Chapitre 3Analyse de complexitéComplexité temporelle, complexité spatiale

0. Vue d'ensemble : Qu'est-ce qu'un algorithme ?

Imaginez que vous cherchez un mot dans un dictionnaire :

  • Méthode 1 : Tourner les pages une par une depuis le début (recherche linéaire)
  • Méthode 2 : Se positionner grâce à la première lettre, puis faire une recherche binaire (recherche binaire)

Les deux méthodes permettent de trouver le mot, mais leur efficacité est radicalement différente. Un algorithme est une méthode de résolution de problème.

Algorithmic Thinking: Ways to Solve ProblemsDifferent strategies fit different kinds of problems
Binary searchEliminate half each time, O(log n)
Time Complexity Quick Reference
O(1)ConstantBest, such as array access
O(log n)LogarithmicVery good, such as binary search
O(n)LinearCommon, such as traversal
O(n log n)LinearithmicAcceptable, such as quicksort
O(n²)QuadraticSlow, such as bubble sort
O(2ⁿ)ExponentialVery slow, such as brute-force recursion
Core idea:Algorithms are methods for solving problems. A good algorithm can improve efficiency by orders of magnitude. Understanding algorithmic thinking matters more than memorizing individual algorithms.

Indicateurs clés d'un algorithme :

IndicateurSignificationPourquoi c'est important
Complexité temporelleTendance du temps d'exécution en fonction de la taille des donnéesPrédire les performances sur de grandes quantités de données
Complexité spatialeTendance de l'utilisation mémoire en fonction de la taille des donnéesÉvaluer la consommation mémoire
CorrectionObtient-il toujours le bon résultatExigence fondamentale d'un algorithme

📊 Lecture ligne par ligne

Complexité temporelle : décrite avec la notation grand O. O(n) signifie que si la taille des données double, le temps double aussi ; O(n²) signifie que si les données doublent, le temps est multiplié par 4.

Complexité spatiale : utilise également la notation grand O. Certains algorithmes échangent de l'espace contre du temps (comme les tables de hachage), d'autres échangent du temps contre de l'espace (comme les algorithmes de compression).

Correction : un algorithme doit produire un résultat correct pour toutes les entrées possibles. Les cas limites (entrée vide, entrée extrêmement grande) sont les plus sujets aux erreurs.


1. Recherche binaire : éliminer la moitié à chaque fois

1.1 Principe de la recherche binaire

💡 Comment fonctionne la recherche binaire ?

Prérequis : les données doivent être triées

Processus :

  1. Trouver l'élément du milieu
  2. Si l'élément du milieu est égal à la cible — trouvé !
  3. Si la cible est inférieure à l'élément du milieu, continuer dans la moitié gauche
  4. Si la cible est supérieure à l'élément du milieu, continuer dans la moitié droite
  5. À chaque étape, éliminer la moitié, jusqu'à trouver ou confirmer l'absence

Complexité temporelle : O(log n)

Analogie quotidienne : Le jeu du nombre à deviner. Je pense à un nombre entre 1 et 100, vous devinez le milieu à chaque fois et je vous dis si c'est plus grand ou plus petit. En 7 essais maximum, vous trouverez (car 2⁷ = 128 > 100).

👇 Essayez par vous-même : La démo ci-dessous illustre le principe de la recherche binaire. Choisissez entre la recherche séquentielle et la recherche binaire pour comparer :

Search AlgorithmsHow to find a target in data
Linear search: check items one by one
3
7
2
9
5
1
8
4
6
10
Target number:
Time complexity: O(n)
Use case: unordered arrays
Performance Comparison
Data sizeLinear searchBinary search
10At most 10 stepsAt most 4 steps
100At most 100 stepsAt most 7 steps
1000At most 1000 stepsAt most 10 steps
10000At most 10000 stepsAt most 14 steps

1.2 Pourquoi la recherche binaire est-elle si rapide ?

Taille des donnéesRecherche linéaireRecherche binaire
100100 fois7 fois
1 0001 000 fois10 fois
1 000 0001 000 000 fois20 fois
1 000 000 0001 000 000 000 fois30 fois

📊 Lecture ligne par ligne

Première colonne (taille des données) : la quantité de données à rechercher. La taille passe de 100 à 1 milliard (multipliée par 10 millions !)

Deuxième colonne (recherche linéaire) : la méthode la plus « basique » — chercher un par un depuis le début. Le nombre de comparaisons est égal à la taille des données ; plus il y a de données, plus il y a de comparaisons.

Troisième colonne (recherche binaire) : la méthode intelligente — éliminer la moitié à chaque fois. Le nombre de comparaisons ne dépend que du logarithme de la taille des données. Même avec 1 milliard de données, il suffit de 30 comparaisons !

Conclusion comparative : lorsque les données atteignent 1 million, la recherche linéaire nécessite 1 million de comparaisons, tandis que la recherche binaire n'en nécessite que 20 — un facteur 50 000 de différence !

📊 La puissance de la croissance logarithmique

La complexité temporelle de la recherche binaire est O(log n), ce qui signifie :

  • 1 milliard de données → 30 comparaisons maximum
  • 1 billion de données → 40 comparaisons maximum

C'est la puissance de la croissance logarithmique : la taille des données est multipliée par 1 000, le nombre de comparaisons n'augmente que de 10.


2. Le tri : transformer le désordre en ordre

2.1 Algorithmes de tri courants

AlgorithmeComplexité temporelleCaractéristiquesCas d'usage
Tri à bullesO(n²)Simple mais lentEnseignement, petites quantités de données
Tri par sélectionO(n²)Simple mais lentPetites quantités de données
Tri par insertionO(n²)Rapide sur des données presque triéesPetites quantités, presque triées
Tri rapideO(n log n)Le plus rapide en pratiqueTri généraliste
Tri fusionO(n log n)Tri stableScénarios nécessitant la stabilité
Tri par tasO(n log n)Tri sur placeScénarios à mémoire limitée

📊 Lecture ligne par ligne

Tri à bulles : l'algorithme de tri le plus fondamental, comme des bulles remontant à la surface de l'eau. Facile à comprendre, mais le plus lent. Adapté pour apprendre le concept de tri, pas pour une utilisation réelle.

Tri par sélection : à chaque fois, sélectionner le plus petit et le placer en premier. Aussi simple, mais effectue le même nombre de comparaisons que les données soient triées ou non.

Tri par insertion : comme lorsqu'on trie ses cartes en main. Insérer chaque élément dans la partie déjà triée. Très efficace sur des données presque triées.

Tri rapide : l'algorithme de tri le plus utilisé en pratique. Le plus rapide en moyenne, mais se dégrade à O(n²) dans le pire cas (données déjà triées).

Tri fusion : utilise le principe « diviser pour régner », toujours O(n log n), mais nécessite de l'espace supplémentaire. Adapté aux scénarios nécessitant un tri stable.

Tri par tas : utilise la structure de données « tas » pour trier, sur place (pas d'espace supplémentaire), mais souvent plus lent que le tri rapide en pratique.

2.2 Pourquoi le tri rapide est-il « rapide » ?

💡 Principe du tri rapide

Idée centrale : Diviser pour régner

  1. Choisir un élément « pivot »
  2. Placer les éléments plus petits que le pivot à gauche, les plus grands à droite
  3. Trier récursivement les parties gauche et droite
  4. Fusionner les résultats

Pourquoi rapide ?

  • Après chaque partitionnement, le pivot est à sa position finale
  • En moyenne, chaque partition élimine environ la moitié des éléments
  • Complexité temporelle O(n log n)

Analogie quotidienne : Ranger une bibliothèque. Sortir un livre, placer les plus minces à gauche et les plus épais à droite. Puis répéter le processus pour chaque pile.

👇 Essayez par vous-même : La démo ci-dessous visualise les algorithmes de tri. Générez un tableau et observez la comparaison entre le tri à bulles et le tri rapide :

Sorting AlgorithmsPut data into order
50
30
70
40
90
20
60
80
10
55
Choose a sorting algorithm
Select a sorting algorithm to start the demo
Time complexity:
Algorithm Comparison
AlgorithmAverage timeWorst timeSpaceStable
Bubble sortO(n²)O(n²)O(1)
Quick sortO(n log n)O(n²)O(log n)
Merge sortO(n log n)O(n log n)O(n)
Insertion sortO(n²)O(n²)O(1)

3. Récursivité : s'appeler soi-même

3.1 L'essence de la récursivité

💡 Qu'est-ce que la récursivité ?

La récursivité est une technique de programmation où une fonction s'appelle elle-même.

Deux éléments clés :

  1. Cas de base : quand arrêter la récursivité ?
  2. Étape récursive : comment décomposer le problème en sous-problèmes plus petits ?

Exemple classique : la factorielle

js
function factorial(n) {
  if (n <= 1) return 1        // Cas de base
  return n * factorial(n - 1) // Étape récursive
}

Analogie quotidienne : Les poupées russes. On ouvre une poupée, à l'intérieur il y en a une plus petite, jusqu'à la plus petite qui ne s'ouvre plus.

3.2 Récursivité vs Itération

CaractéristiqueRécursivitéItération (boucle)
Concision du codeGénéralement plus concisPotentiellement plus complexe
Consommation mémoirePlus élevée (pile d'appels)Plus faible
PerformanceLégèrement plus lent (overhead des appels)Plus rapide
Cas d'usageParcours d'arbres, diviser pour régnerTâches répétitives simples

📊 Lecture ligne par ligne

Concision du code : la récursivité peut souvent exprimer une logique complexe en quelques lignes (comme le parcours d'un arbre), tandis que les boucles peuvent nécessiter plus de variables et d'imbrications.

Consommation mémoire : la récursivité utilise une « pile d'appels » pour stocker les informations de chaque niveau, comme empiler des assiettes — à chaque appel récursif, on ajoute une assiette. Les boucles n'ont pas cet overhead.

Performance : chaque appel de fonction a un overhead (passage de paramètres, opérations sur la pile, etc.), donc la récursivité est généralement légèrement plus lente que les boucles.

Cas d'usage : la récursivité excelle pour les problèmes de structure intrinsèquement récursive (comme les arbres de fichiers, le DOM) ; les boucles sont adaptées aux opérations répétitives simples (comme le parcours de tableaux).

⚠️ Pièges de la récursivité

Débordement de pile (Stack Overflow) : la profondeur de récursivité est trop importante, l'espace de la pile d'appels est épuisé.

Solutions :

  • Passer à l'itération
  • Utiliser l'optimisation de la récursivité terminale (prise en charge par certains langages)
  • Limiter la profondeur de récursivité

👇 Essayez par vous-même : La démo ci-dessous montre le processus d'appel récursif. Observez comment une fonction s'appelle elle-même :

Recursive Thinking: A Function Calls ItselfBreak a large problem into smaller problems of the same kind
🪆
Nested dolls
Open a large doll and there is a smaller doll inside
Open that one and there is an even smaller one, until the smallest case
That is recursion.
Recursive examples
Factorial: n! = n × (n-1)!
5! = 5 × 4!
4! = 4 × 3!
3! = 3 × 2!
2! = 2 × 1!
1! = 1 (base case)
↑ return 1
↑ return 2 × 1 = 2
↑ return 3 × 2 = 6
↑ return 4 × 6 = 24
↑ return 5 × 24 = 120
Three Elements of Recursion
1
Base case
When should recursion stop? There must be a termination condition.
Example: 1! = 1
2
Recursive call
How does the problem get smaller? Call the same function on a smaller case.
Example: turn n! into (n-1)!
3
Return result
How does the current problem use the result of the subproblem?
Example: the result of n × (n-1)!
✓ Pros
  • Concise code
  • Naturally expresses recursive structures
  • Good for tree and graph traversal
✗ Cons
  • May repeat work
  • Uses stack space
  • Can be harder to debug

4. Algorithmes gloutons : choisir l'optimum à chaque étape

4.1 La pensée gloutonne

💡 Qu'est-ce qu'un algorithme glouton ?

Les algorithmes gloutons font à chaque étape le choix qui semble localement optimal, en espérant obtenir une solution globalement optimale.

Conditions d'application :

  1. Propriété de choix glouton : un optimum local conduit à un optimum global
  2. Sous-structure optimale : la solution optimale du problème contient les solutions optimales des sous-problèmes

Exemple classique : le rendu de monnaie

  • Objectif : composer un montant donné avec le moins de pièces possible
  • Stratégie gloutonne : toujours choisir la plus grande pièce
  • Résultat : 67 € = 50 + 10 + 5 + 1 + 1 (5 pièces)

Analogie quotidienne : En montagne, choisir à chaque fois le chemin le plus raide. On n'atteint pas nécessairement le sommet le plus haut, mais généralement une bonne position.

4.2 Les limites de l'approche gloutonne

⚠️ Le glouton ne garantit pas toujours l'optimum

Contre-exemple : rendu de monnaie

Si les valeurs des pièces sont [1, 3, 4] et qu'on doit composer 6 € :

  • Glouton : 4 + 1 + 1 = 3 pièces
  • Optimal : 3 + 3 = 2 pièces

L'algorithme glouton a échoué ici !

Leçon : les algorithmes gloutons sont simples et efficaces, mais ne trouvent pas toujours la solution optimale. Avant de les utiliser, il faut prouver que le problème satisfait les conditions gloutonnes.

👇 Essayez par vous-même : La démo ci-dessous montre les effets pratiques de l'algorithme glouton. Essayez différentes combinaisons de pièces et observez les performances de la stratégie gloutonne :

Greedy Algorithms: Choose the Best Current MoveLocal optimum → global optimum?
A greedy algorithm chooses the best option available at each step
It hopes a series of local choices reaches a global optimum
Classic problems
Coin Change Problem
Change needed: 37
20
× 1 = 20
10
× 1 = 10
5
× 1 = 5
1
× 2 = 2
Needs 5 coins total
✓ Greedy strategy: choose the largest coin each time
✓ Works for currencies such as RMB and USD
Greedy vs Dynamic Programming
FeatureGreedy algorithmDynamic programming
Decision styleChoose the current best each stepConsider all possibilities and choose the best
OptimalityMay not be globally optimalGuarantees a global optimum
Time complexityO(n) or O(n log n)O(n²) or higher
Best fitLocal optimum → global optimumOverlapping subproblems
✓ Pros
  • Simple to implement
  • Efficient
  • Low space complexity
✗ Cons
  • Does not always guarantee a global optimum
  • Limited applicability
  • Requires an optimality proof

5. Paradigmes de conception d'algorithmes

ParadigmeIdéeAlgorithmes typiquesProblèmes applicables
Diviser pour régnerDécomposer le problème en sous-problèmesTri rapide, tri fusionProblèmes décomposables
GloutonChoisir l'optimum à chaque étapeArbre couvrant minimum, codage de HuffmanProblèmes avec propriété gloutonne
Programmation dynamiqueMémoriser les solutions des sous-problèmesProblème du sac à dos, plus court cheminSous-problèmes chevauchants
Retour arrière (Backtracking)Essayer, revenir en arrière si ça ne marche pasProblème des huit dames, toutes les permutationsProblèmes de recherche

📊 Lecture ligne par ligne

Diviser pour régner : décomposer les grands problèmes en petits, les résoudre séparément puis les fusionner. Comme faire le ménage : salon, chambre, cuisine séparément, et à la fin tout est propre.

Glouton : à chaque étape, choisir le mieux actuellement sans penser aux conséquences à long terme. Comme manger d'abord son plat préféré — pas forcément optimal, mais rapide.

Programmation dynamique : retenir les résultats intermédiaires pour éviter les calculs redondants. Comme prendre des notes : la prochaine fois qu'on rencontre le même problème, on consulte directement la réponse.

Retour arrière : quand on est bloqué, revenir en arrière et réessayer. Comme dans un labyrinthe : ce chemin est bloqué, retourner au carrefour précédent et en essayer un autre.

👇 Essayez par vous-même : La démo ci-dessous présente les caractéristiques et les domaines d'application des différents paradigmes de conception d'algorithmes :

Algorithm Design ParadigmsCommon patterns for solving problems
Algorithm design paradigms are general strategies for solving problems. Learning them helps you find solution ideas quickly.
✂️
Divide and conquer
Divide, solve, combine
📊
Dynamic programming
Store results to avoid repetition
🎯
Greedy
Local optimum
🔙
Backtracking
Try and retreat
✂️Divide and conquer
Core idea
Split a large problem into smaller independent problems, solve them recursively, then combine the results.
Use cases
Array sortingMatrix multiplicationLarge integer arithmetic
Classic problems
📝
Merge sort
📝
Quick sort
📝
Binary search
📝
Strassen matrix multiplication
Time complexity
O(n log n)
Often much faster than brute force
Paradigm Comparison
ParadigmCore strategyOptimalityUse case
✂️ Divide and conquerSplit → recurse → combineGuarantees optimumProblems that split independently
📊 Dynamic programmingStore subproblem answersGuarantees optimumOverlapping subproblems
🎯 GreedyChoose the best each timeNot always optimalLocal optimum → global optimum
🔙 BacktrackingDepth-first searchGuarantees optimumSmall search spaces that need enumeration
How to choose the right paradigm?
1
Analyze problem features
Are there overlapping subproblems? Is there optimal substructure?
2
Decide whether an optimum is required
Greedy is not always optimal; dynamic programming guarantees an optimum.
3
Consider input size
Backtracking fits small inputs, while divide and conquer fits larger inputs.

6. Résumé : L'algorithme, un art de résoudre les problèmes

Résumons les différentes pensées algorithmiques avec une analogie :

PenséeAnalogiePoint clé
Recherche binaireDevinette numériqueÉliminer la moitié à chaque fois
TriRanger une bibliothèqueCréer de l'ordre
RécursivitéPoupées russesRéduire le grand au petit
GloutonChoisir son chemin en montagneOptimum local

💡 Insight clé

L'essence des algorithmes est l'équilibre entre « efficacité » et « correction ».

  • Un bon algorithme peut améliorer les performances de plusieurs ordres de grandeur
  • Mais l'optimisation excessive peut introduire de la complexité
  • D'abord assurer la correction, puis viser l'efficacité

Comprendre la pensée algorithmique est plus important que de mémoriser des algorithmes spécifiques :

  • Diviser pour régner : décomposer les grands problèmes en petits
  • Glouton : choisir l'optimum à chaque étape
  • Programmation dynamique : mémoriser les solutions des sous-problèmes
  • Retour arrière : essayer, revenir en arrière si ça ne marche pas

Lectures complémentaires

  • Introduction à l'algorithmique : manuel classique pour l'apprentissage systématique des algorithmes
  • LeetCode : améliorer ses compétences algorithmiques par la pratique
  • Visualisation d'algorithmes : comprendre intuitivement l'exécution des algorithmes
  • Algorithmes de compétition : apprendre des techniques algorithmiques avancées