diff --git a/cours/04 - Les dictionnaires/cours.js b/cours/04 - Les dictionnaires/cours.js new file mode 100644 index 0000000..741846c --- /dev/null +++ b/cours/04 - Les dictionnaires/cours.js @@ -0,0 +1,59 @@ +/* Les structures de données non linéaires : les dictionnaires */ + +// Création d'un dictionnaire vide (un simple objet !) +let identite = {}; +console.log(identite); + +// Ajout d'une nouvelle association +identite.prenom = "Jérémy"; +identite["age"] = 36; +identite.developpeur = true; +console.log(identite); + +// Lecture d'une valeur par sa clé +console.log(identite.prenom); +console.log(identite["developpeur"]); + +// Modification d'une valeur par sa clé +console.log("Age avant :", identite.age); +identite.age = 42; +console.log("Age après :", identite.age); + +// Suppression d'une association +delete identite.age; +console.log(identite); + +// Type de la clé, chaîne ou nombre +identite[42] = "quarante-deux"; +console.log(identite); + +// Parcourir les clés avec for...in +for (let cle in identite) { + console.log("for...in", cle, "=>", identite[cle]); +} + +// Récupérer toutes les clés dans un tableau +console.log("tableau des clés :", Object.keys(identite)); +// Récupérer toutes les valeurs dans un tableau +console.log("tableau des valeurs :", Object.values(identite)); + +// Récupérer tous les couples clé -> valeur +let couples = Object.entries(identite); +console.log(couples); + +// Regardons le détail avec une boucle for classique +for (let i = 0; i < couples.length; i++) { + let couple = couples[i]; + console.log("couple numéro", i, couple); +} + +// Même chose avec une boucle for...of +for (let couple of couples) { + console.log("for...of couple :", couple); +} + +// Même chose avec l'array destructuring +// let [cle, valeur] = ["ma clé", "ma valeur"]; +for (let [cle, valeur] of couples) { + console.log(cle, "=>", valeur); +} \ No newline at end of file diff --git a/cours/05 - Le type Map/Object VS Map.js b/cours/05 - Le type Map/Object VS Map.js new file mode 100644 index 0000000..bbc83db --- /dev/null +++ b/cours/05 - Le type Map/Object VS Map.js @@ -0,0 +1,85 @@ +/* Quelles différences : Object VS Map ? */ + +// Histoire (avant / après ES6) +// Le type Map et les méthodes Object.keys(), Object.values() et Object.entries() +// ont été créés dans la version ES6 + +// Mêmes clés et valeurs... +let donnees = [ + ['s','v-s'], ['a','v-a'], ['c','v-c'], // "sac" + ['bebe','v-bebe'], ['adulte','v-adulte'], // "bebe" => "adulte" + ['11','v-11'], ['1','v-1'], ['2','v-2'], ['12','v-12'] // 11 1 2 12 +]; +let objet = {}; +let map = new Map(); +// Dans le même ordre d'insertion... +for (let [cle, valeur] of donnees) { + objet[cle] = valeur; + map.set(cle, valeur); +} + +// Ordre des clés : non spécifié avant ES6 +console.log("\n-- Ordre des clés"); +// ❌ Ordre complexe (nombres croissants puis strings & symboles dans l'ordre d'insertion) +console.log(Object.entries(objet)); +// ✅ Ordre d'insertion +console.log(Array.from(map.entries())); + +// Lecture de la taille du dictionnaire +console.log("\n-- Taille du dictionnaire"); +console.log("Object:", Object.keys(objet).length); +console.log("Map:", map.size); + +// Itération sur les clés / valeurs +// ❌ Il faut récupérer les clés / valeurs avant +console.log("\n-- Itération sur les couples"); +for (let [cle, valeur] of Object.entries(objet)) { + console.log("Object:", cle, valeur); +} +// ✅ Map est itérable : on itère dessus directement +for (let [cle, valeur] of map) { + console.log("Map:", cle, valeur); +} + +// Clés accidentelles (chaîne de prototype) +console.log("\n-- Clés accidentelles"); +let chaine = "Phrase constructor toString valueOf fin"; +let compteurObjet = {}; +let compteurMap = new Map(); +for (let mot of chaine.split(' ')) { + if (compteurObjet[mot] !== undefined) compteurObjet[mot]++; + else compteurObjet[mot] = 1; + if (compteurMap.has(mot)) compteurMap.set(mot, compteurMap.get(mot) + 1); + else compteurMap.set(mot, 1); +} +// ❌ Object contient des propriétés héritées (conflits) +console.log(compteurObjet); +// ✅ Map ne contient que ce qu'on lui ajoute +console.log(compteurMap); + +// Type des clés +console.log("\n-- Type des clés"); +// ❌ Object est limité aux chaînes +let jeremy = { prenom: "Jérémy", statut: "admin" }; +let tom = { prenom: "Tom", statut: "dev" }; +let identifiantsObjet = {}; +identifiantsObjet[jeremy] = 42; +identifiantsObjet[tom] = 1337; +console.log(identifiantsObjet); +// ✅ Map peut avoir des clés de n'importe quel type (objets, tableaux, sets etc.) +let identifiantsMap = new Map(); +identifiantsMap.set(jeremy, 42); +identifiantsMap.set(tom, 1337); +console.log(identifiantsMap); + +// 🚨 NOTE IMPORTANTE : objet en clé === Référence ! +console.log("\n-- Attention aux références !"); +let personne = { prenom: "Jérémy", statut: "admin" }; +console.log("jeremy: ", jeremy); +console.log("personne: ", personne); +console.log(identifiantsMap.get(jeremy)); +console.log(identifiantsMap.get(personne)); + +// ✅ Map est optimisé pour les ajouts/suppressions fréquentes et les gros volumes + +// Ce qu'il faut retenir est SIMPLE : préférez l'utilisation du type Map au lieu d'Object quand vous devez utiliser un dictionnaire ! \ No newline at end of file diff --git a/cours/05 - Le type Map/cours.js b/cours/05 - Le type Map/cours.js new file mode 100644 index 0000000..e6c3b64 --- /dev/null +++ b/cours/05 - Le type Map/cours.js @@ -0,0 +1,120 @@ +/* Les structures de données non linéaires : le type Map */ + +// Création d'un dictionnaire vide +console.log("-- Création d'un dictionnaire vide"); +let identite = new Map(); +console.log(identite); + +// Ajout d'une nouvelle association avec set() +console.log("\n-- Ajout d'une nouvelle association"); +console.log('✅ avec .set()'); +identite.set("prenom", "Jérémy"); +identite.set("age", 36); +identite.set("developpeur", true); +// 🚨 ERREUR FREQUENTE +console.log('❌ identite.nombre = 42 ou identite["nombre"] = 42'); +identite.nombre = 42; +console.log(identite); + +// Lecture d'une valeur par sa clé avec get() +console.log("\n-- Lecture d'une valeur par sa clé"); +console.log('✅ avec .get() =>', identite.get("prenom")); +// 🚨 ERREUR FREQUENTE +console.log('❌ identite.prenom ou identite["prenom"] =>', identite.prenom); + +// Modification d'une valeur par sa clé avec set() +console.log("\n-- Modification d'une valeur"); +console.log("Age avant :", identite.get("age")); +console.log('✅ avec .set("age", 42)'); +identite.set("age", 42); +console.log('✅ Age après : identite.get("age") =>', identite.get("age")); +// 🚨 ERREUR FREQUENTE +console.log('❌ Age après : identite.age ou identite["age"] =>', identite.age); + +// Suppression d'une association avec delete() +console.log("\n-- Suppression de la clé age"); +// 🚨 ERREUR FREQUENTE +console.log("❌ delete identite.age"); +delete identite.age; +console.log(identite); +console.log('✅ avec .delete()'); +identite.delete("age"); +console.log(identite); + +// Parcourir les associations avec for...of +console.log("\n-- Parcourir les associations"); +console.log("✅ avec for...of"); +for (let paire of identite) { + console.log(paire); +} +// 🚨 ERREUR FREQUENTE +console.log("❌ avec for...in"); +for (let propriete in identite) { + console.log(propriete); +} + +// Récupérer toutes les clés dans un tableau +console.log("\n-- Récupérer les clés dans un tableau"); +console.log("✅ avec .keys()"); +console.log("Itérateur sur les clés :", identite.keys()); +console.log("Transformation en tableau de clés :", Array.from(identite.keys())); +console.log("❌ avec Object.keys()"); +console.log("Tableau des propriétés :", Object.keys(identite)); + + +// Récupérer toutes les valeurs dans un tableau +console.log("\n-- Récupérer les valeurs dans un tableau"); +console.log("✅ avec .values()"); +console.log("Itérateur sur les valeurs des clés :", identite.values()); +console.log("Transformation en tableau des valeurs des clés :", Array.from(identite.values())); +console.log("❌ avec Object.values()"); +console.log("Tableau des valeurs des propriétés :", Object.values(identite)); + +// Récupérer tous les couples clé -> valeur +console.log("\n-- Récupérer les couples clé -> valeur"); +console.log("✅ avec .entries()"); +console.log("Itérateur sur les couples clé -> valeur :", identite.entries()); +let tableauCouples = Array.from(identite.entries()); +console.log("Transformation en tableau des couples clé -> valeur :", tableauCouples); +console.log("Contenu de ce tableau :"); +for (let couple of tableauCouples) { + console.log(couple); +} +console.log("❌ avec Object.entries()"); +let entries = Object.entries(identite); +console.log("Tableau des couples propriété -> valeur :", entries); +console.log("Contenu de ce tableau :"); +for (let couple of entries) { + console.log(couple); +} + +/* Ce qu'il faut retenir */ +// Utilisez TOUJOURS les méthodes de l'objet Map ! +// ✅ .get() .set() .delete() .has() .keys() .values() .entries() +// ❌ Object.keys(), Object.values(), Object.entries(), delete + +// Point important : .keys(), .values() et .entries() renvoient un itérateur +// Pour le transformer en tableau : Array.from(itérateur) + +// On peut aussi utiliser directement les itérateurs avec des boucles for...of +console.log("\n-- Itérer sur les itérateurs avec for...of"); +console.log("Liste des clés : for...of identite.keys()"); +for (let cle of identite.keys()) { + console.log(cle); +} + +console.log("\nListe des valeurs : for...of identite.values()"); +for (let valeur of identite.values()) { + console.log(valeur); +} + +console.log("\nListe des couples : for...of identite.entries()"); +for (let couples of identite.entries()) { + console.log(couples); +} + +// Lisez la doc du MDN sur Map pour en savoir plus : +// https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Map + +/* Quelles différences : Object VS Map ? */ +// Ce sera l'objet de la prochaine leçon 😁 \ No newline at end of file diff --git a/cours/06 - Calcul temps execution/cours.js b/cours/06 - Calcul temps execution/cours.js new file mode 100644 index 0000000..ba182bd --- /dev/null +++ b/cours/06 - Calcul temps execution/cours.js @@ -0,0 +1,30 @@ +/* Calcul de vitesse d'exécution d'un algorithme */ + +function calculerSommeDe1aN(N) { // Nombre d'opérations + let somme = 0; // + for (let i = 1; i <= N; i++) { // + somme = somme + i; // + } + return somme; // +} + +function calculerSommeDe1aNOptimal(N) { // Nombre d'opérations + return N * (N + 1) / 2; // 4 +} + +// (Séparateur numérique "_" depuis ES12) +const N = 100_000_000; // Erreur de précision à 1 Md + +// Exécution algorithme optimal +let maintenant = Date.now(); +let res = calculerSommeDe1aNOptimal(N); +let tempsOptimal = Date.now() - maintenant; +console.log("Optimal :", res, "(" + tempsOptimal, "ms)"); + +// Exécution algorithme naïf +maintenant = Date.now(); +res = calculerSommeDe1aN(N); +let tempsNaif = Date.now() - maintenant; +console.log(" Naif :", res, "(" + tempsNaif, "ms)"); + +// Prochain exercice : recherche par dichotomie \ No newline at end of file diff --git a/cours/07 - Recherche dichotomique/cours.js b/cours/07 - Recherche dichotomique/cours.js new file mode 100644 index 0000000..62ea597 --- /dev/null +++ b/cours/07 - Recherche dichotomique/cours.js @@ -0,0 +1,98 @@ +/* Temps d'exécution recherche par dichotomie */ + +let nombreMax = 1000000; +let nombreATrouver = Math.floor(Math.random() * nombreMax) + 1; +console.log("Nombre :", nombreATrouver); + +let tentativesLineaire = trouverNombreLineaire(nombreMax); +console.log("Algorithme linéaire", tentativesLineaire, "tentatives"); +let tentativesPseudoAleatoire = trouverNombrePseudoAleatoire(nombreMax); +console.log("Algo pseudo-aléatoire", tentativesPseudoAleatoire, "tentatives"); +let tentativeDichotomie = trouverNombreDichotomie(nombreMax); +console.log("Algorithme dichotomie", tentativeDichotomie, "tentatives"); + +function verifier(nombre) { + if (nombre === nombreATrouver) { + return "ok"; + } else if (nombre < nombreATrouver) { + return "superieur"; + } else { + return "inferieur"; + } +} + +function trouverNombreLineaire(nombreMax) { + let tentatives = 1; + for (let choix = 1; choix <= nombreMax; choix++) { + if (verifier(choix) === "ok") { + return tentatives; + } else { + tentatives++; + } + } +} + +function trouverNombrePseudoAleatoire(nombreMax) { + let reponse = ""; + let tentatives = 0; + let intervalleMin = 1; + let intervalleMax = nombreMax; + do { + // On choisit un nombre aléatoire dans le nouvel intervalle + let choix = Math.floor(Math.random() * (intervalleMax - intervalleMin + 1)) + intervalleMin; + reponse = verifier(choix); + // Exemple : [1 2 3 4 5 6] + if (reponse === "inferieur") { + intervalleMax = choix - 1; + } else if (reponse === "superieur") { + intervalleMin = choix + 1; + } + tentatives++; + } while (reponse !== "ok"); + return tentatives; +} + +function trouverNombreDichotomie(nombreMax) { + let reponse = ""; + let tentatives = 0; + let intervalleMin = 1; + let intervalleMax = nombreMax; + do { + // On prend le milieu de l'intervalle pour le couper en 2 + // Exemple : [1 2 3 4 5 6] et [3 4 5 6 7] + let choix = Math.ceil((intervalleMin + intervalleMax) / 2); + reponse = verifier(choix); + if (reponse === "inferieur") { + intervalleMax = choix - 1; + } else if (reponse === "superieur") { + intervalleMin = choix + 1; + } + tentatives++; + } while (reponse !== "ok"); + return tentatives; +} + +/* Division successives par 2 des intervalles + +1 2 3 4 5 6 7 +1 2 3 +1 => Réponse pire cas en 3 tentatives + +1 2 3 4 5 6 7 9 8 10 11 12 13 +1 2 3 4 5 6 +1 2 3 +1 => Réponse pire cas en 4 tentatives + +1 2 3 4 5 6 7 9 8 10 11 12 13 14 15 16 17 18 +1 2 3 4 5 6 7 9 +1 2 3 4 +1 2 +1 => Réponse pire cas en 5 tentatives + +1 * 2 * 2 * 2 * 2 ... + +2 ** X (2^X) >= nombreMax +log2(2**X) >= log2(nombreMax) +X >= log2(nombreMax) + +*/ \ No newline at end of file diff --git a/cours/07 - Recherche dichotomique/index.html b/cours/07 - Recherche dichotomique/index.html new file mode 100644 index 0000000..3791ab8 --- /dev/null +++ b/cours/07 - Recherche dichotomique/index.html @@ -0,0 +1,27 @@ + + +
+ + + + +
+ Pour répondre à ces exercices, tapez votre code dans le fichier exercices.js
.
+
+ Pensez à bien lire l'intégralité de la documentation pour faire les exercices avant de commencer. +
++ Dernier point, lorsque vous trouvez la solution d'un exercice par vous-même, allez tout de même voir la solution que + je propose : + vous apprendrez peut-être une autre façon de faire plus intéressante ! +
+ +Il se peut que vous rencontriez certains problèmes techniques pour valider les exercices.
+ Lisez leur solution au moins une fois avant de commencer les exercices.
Si certains exercices précédemment validés en vert redeviennent gris et "En attente de code à tester" : c'est + qu'il y + a une erreur de syntaxe dans votre code. Solution : ouvrez la console développeur pour trouver l'erreur.
+Sur Windows ou Linux : depuis Chrome, tapez Ctrl + Shift + J
ou allez dans le
+ menu (les 3
+ petits points verticaux en haut à droite de la fenêtre de Chrome) puis allez dans Plus d'outils > Outils de
+ développement.
Sur MacOS : depuis Chrome, tapez ⌘ + ⌥ + J
(Commande + Option + J) ou bien
+ allez dans le
+ menu Afficher > Options pour les développeurs > Console JavaScript.
Le message d'erreur apparaît en rouge et est en anglais, il faudra le traduire. L'erreur vient du fichier
+ noté sur la droite au numéro de ligne indiqué. Ouvrez ce fichier dans VSCode, rendez-vous à la ligne indiquée
+ et corrigez l'erreur.
+
+
Si la page web ne répond plus (les clics n'ont aucun effet) ou si l'icône dans l'onglet tourne indéfiniment : + c'est qu'il y a une boucle infinie dans votre code. Solution : corrigez votre code puis actualisez le site web. +
+Si vous avez fait une boucle infinie, l'icône de votre onglet va tourner comme ça indéfiniment
+ :
Voici à quoi peut ressembler une boucle infinie dans du code :
+
+ let poidsClient = prochainClient.mesurerPoids();
+
+ while (poidsClient !== 0) {
+ console.log(poidsClient);
+ }
+
Le problème ici, c'est que la condition de répétition de la boucle poidsClient !== 0
est
+ toujours vraie ! En effet, on ne modifie pas la valeur de poidsClient
à l'intérieur de la boucle,
+ par
+ conséquent celle-ci s'exécutera sans jamais s'arrêter : vous avez fait une boucle infinie !
Voici les étapes à respecter pour se sortir de ce problème :
+Arrêter le processus
en bas à droite.
+ Dans les paramètres, je vous conseille d'utiliser l'option onFocusChange
pour la sauvegarde
+ automatique.
En effet, si vous avez l'option afterDelay
de sélectionnée, ça veut dire que VSCode va
+ sauvegarder votre code toutes les X secondes. Le site web va se recharger automatiquement à ce moment là et si
+ votre code actuel génère une boucle infinie, Chrome va planter !
Pour éviter ce problème, utilisez l'option onFocusChange
qui indique à VSCode de sauvegarder
+ votre fichier seulement si vous changez de fenêtre ou d'onglet. Vous pouvez aussi sauvegarder manuellemment
+ avec ⌘ + S
sur MacOS ou Ctrl + S
sur Windows ou Linux quand vous voulez vérifier que
+ votre code fonctionne.
Si vous souhaitez déboguer votre code, utilisez le débogueur de Chrome comme je l'explique dans cette vidéo.
+Si vous êtes bloqué et qu'aucune solution mentionnée ne fonctionne, envoyez-moi un email sur jeremy@javascriptdezero.com en précisant le numéro de la leçon + sur laquelle vous travaillez et le numéro de l'exercice qui pose problème.
+Tous les exercices fonctionnent sur le même principe : vous devez rédiger une fonction récursive à partir de zéro et celle-ci devra renvoyer (avec le mot-clé return
) la solution de l'énoncé.
+ Créez une fonction récursive sommeJusquaN(N)
qui renvoie la somme des nombres de 1 à N
inclus. La valeur de N
sera toujours supérieure ou égale à 1
.
+
Exemples :
+sommeJusquaN(1)
renverra 1
.sommeJusquaN(3)
renverra 6
.sommeJusquaN(42)
renverra 903
.
+ Créez une fonction récursive puissance(nombre, p)
qui renvoie le nombre
porté à la puissance p
. Autrement dit, la fonction doit calculer nombre ** p
(qu'on écrit aussi nombre^p
).
+
La valeur de nombre
sera toujours un entier supérieur ou égal à 1
. La valeur de p
sera toujours un entier supérieur ou égal à 0
.
Rappel : tout nombre porté à la puissance 0
vaut toujours 1
. Autrement dit n ** 0 = 1
.
Exemples :
+puissance(5, 0)
renverra 1
.puissance(5, 1)
renverra 5
.puissance(5, 2)
renverra 25
.puissance(5, 4)
renverra 625
.puissance(42, 2)
renverra 1764
.Voici un grand classique de la littérature informatique pour expliquer la récursivité : la fonction factorielle.
+ +La factorielle d'un nombre N
se note N!
. Celle-ci vaut N! = N * (N - 1) * (N - 2) * ... * 1.
Avec un exemple on comprend mieux, calculons la factorielle du nombre 5
. On obtient : 5! = 5 * 4 * 3 * 2 * 1
ce qui fait au total 120
.
+ Créez une fonction récursive factorielle(nombre)
qui renvoie la factorielle de nombre
. Notez que la factorielle de 1 vaut 1 : 1! = 1
.
+
La valeur de nombre
sera toujours un entier supérieur ou égal à 1
.
Exemples :
+factorielle(1)
renverra 1
.factorielle(5)
renverra 120
.factorielle(7)
renverra 5040
.La suite de Fibonacci est une suite de nombres entiers dont chaque nombre est la somme des 2 nombres qui le précèdent. Autrement dit, pour trouver le prochain nombre de cette suite, il suffit d'ajouter les 2 derniers nombres de celle-ci (on va voir des exemples ci-dessous).
+ +Par définition, on fixe le 1er nombre de la suite de Fibonacci à 0
et le second nombre à 1
. Par conséquent le 3ème nombre peut être calculé en faisant 0 + 1 = 1
. Le 4ème nombre peut être calculé en faisant 1 + 1 = 2
. Le 5ème nombre peut être calculé en faisant 1 + 2 = 3
etc.
Le début de la suite de Fibonacci est donc : 0, 1, 1, 2, 3, 5, 8, 13, 21, 34...
+ Créez une fonction récursive fibonacci(N)
qui renvoie le Nième
nombre de la suite de Fibonacci. La valeur de N
sera toujours un entier supérieur ou égal à 0
. Attention : le 1er nombre de la suite doit être retourné quand N
vaut 0
(on compte à partir de 0 !).
+
Exemples :
+fibonacci(0)
renverra 0
.fibonacci(1)
renverra 1
.fibonacci(2)
renverra 1
.fibonacci(3)
renverra 2
.fibonacci(4)
renverra 3
.fibonacci(5)
renverra 5
.fibonacci(6)
renverra 8
.
+ Créez une fonction récursive retourner(chaine)
qui renvoie la chaine
de caractères à l'envers.
+
Exemples :
+retourner("JavaScript")
renverra tpircSavaJ
.retourner("Bonjour")
renverra ruojnoB
.
+ Créez une fonction récursive dichotomie(liste, nombreATrouver, posDebut, posFin)
qui renvoie la position (l'index) du nombreATrouver
dans la liste
.
+
La liste
est un tableau qui contient des entiers supérieurs ou égaux à 0
. Point important : est elle triée par ordre croissant.
posDebut
vaudra 0
lors de l'appel initial de la fonction dichotomie par mon code de test.posFin
vaudra liste.length - 1
lors de l'appel initial de la fonction dichotomie par mon code de test.nombreATrouver
se trouve toujours dans la liste
Exemples :
+dichotomie([0,1,3,6,8,12,45,59,89,100], 12, 0, 9)
renverra 5
.dichotomie([0,12,45,59,89], 59, 0, 4)
renverra 3
.dichotomie([1,3,4,5,6,8,12,42], 3, 0, 7)
renverra 1
.Tests réussis : ${pourcentageReussi}%
`; + } + return messageErreur; +} + +function formateArgumentEtValeurHtml(nomArgument, valeur) { + return `${nomArgument} =${formaterObjet(valeur)}
`;
+}
\ No newline at end of file
diff --git a/cours/13 - Tri a bulles/algorithme.pseudo b/cours/13 - Tri a bulles/algorithme.pseudo
new file mode 100644
index 0000000..a0a8a90
--- /dev/null
+++ b/cours/13 - Tri a bulles/algorithme.pseudo
@@ -0,0 +1,17 @@
+/* Algorithme du tri à bulles
+
+1. Comparer chaque couple de valeurs
+2. Si valeur gauche > valeur droite, échanger les valeurs
+3. Répéter jusqu'à ce que le tableau soit trié
+
+💡 Analogie : bulles de boisson gazeuse 🍺🥂 qui remontent à la surface.
+Les bulles sont les grands nombres, la surface est la droite du tableau.
+*/
+
+// Animation sur https://visualgo.net/en/sorting
+
+// Complexité quadratique : O(n^2) 🐌
+
+// Cas de test
+// tableau = [6, 12, 1, 18, 5, 25] => [1, 5, 6, 12, 18, 25]
+
diff --git a/cours/13 - Tri a bulles/cours.js b/cours/13 - Tri a bulles/cours.js
new file mode 100644
index 0000000..cdd0ef2
--- /dev/null
+++ b/cours/13 - Tri a bulles/cours.js
@@ -0,0 +1,7 @@
+/* Tri à bulles (Bubble sort) */
+
+let tableau = [6, 12, 1, 18, 5, 25];
+
+// A implémenter
+
+console.log(tableau); // [1, 5, 6, 12, 18, 25]
\ No newline at end of file
diff --git a/cours/14 - Tri par selection/algorithme.pseudo b/cours/14 - Tri par selection/algorithme.pseudo
new file mode 100644
index 0000000..f2f7ba9
--- /dev/null
+++ b/cours/14 - Tri par selection/algorithme.pseudo
@@ -0,0 +1,16 @@
+/* Algorithme de tri par sélection
+
+1. Démarrer en position = 0 (1er élément du tableau)
+2. Chercher le plus petit élément du tableau à partir de position + 1
+3. Échanger l'élément à position avec le plus petit élément trouvé
+4. Répéter avec l'élément suivant dans le tableau
+
+*/
+
+// Animation sur https://visualgo.net/en/sorting
+
+// Complexité quadratique : O(n^2) 🐌
+
+// Cas de test
+// tableau = [1, 12, 6, 18, 5, 25] => [1, 5, 6, 12, 18, 25]
+
diff --git a/cours/14 - Tri par selection/cours.js b/cours/14 - Tri par selection/cours.js
new file mode 100644
index 0000000..68a071e
--- /dev/null
+++ b/cours/14 - Tri par selection/cours.js
@@ -0,0 +1,7 @@
+/* Tri par sélection (Selection sort) */
+
+let tableau = [6, 12, 1, 18, 5, 25];
+
+// A implémenter
+
+console.log(tableau); // [1, 5, 6, 12, 18, 25]
\ No newline at end of file
diff --git a/cours/15 - Tri par insertion/algorithme.pseudo b/cours/15 - Tri par insertion/algorithme.pseudo
new file mode 100644
index 0000000..064167e
--- /dev/null
+++ b/cours/15 - Tri par insertion/algorithme.pseudo
@@ -0,0 +1,17 @@
+/* Algorithme de tri par insertion
+
+1. Démarrer en position = 1 (2ème élément du tableau)
+2. Comparer l'élément courant avec tous ceux à gauche
+3. Placer l'élément courant à la bonne place
+4. Passer à l'élément suivant jusqu'à la fin du tableau
+
+💡 Analogie : tri d'un jeu de carte dans sa main
+*/
+
+// Animation sur https://visualgo.net/en/sorting
+
+// Complexité quadratique : O(n^2) 🐌
+
+// Cas de test
+// tableau = [1, 5, 6, 12, 18, 25] => [1, 5, 6, 12, 18, 25]
+
diff --git a/cours/15 - Tri par insertion/cours.js b/cours/15 - Tri par insertion/cours.js
new file mode 100644
index 0000000..b873db7
--- /dev/null
+++ b/cours/15 - Tri par insertion/cours.js
@@ -0,0 +1,7 @@
+/* Tri par insertion (Insertion sort) */
+
+let tableau = [1, 5, 6, 12, 18, 25];
+
+// A implémenter
+
+console.log(tableau); // [1, 5, 6, 12, 18, 25]
\ No newline at end of file
diff --git a/cours/16 - Permutations/algo-heap.js b/cours/16 - Permutations/algo-heap.js
new file mode 100644
index 0000000..e69de29
diff --git a/cours/16 - Permutations/algo-heap.pseudo b/cours/16 - Permutations/algo-heap.pseudo
new file mode 100644
index 0000000..20c5c9f
--- /dev/null
+++ b/cours/16 - Permutations/algo-heap.pseudo
@@ -0,0 +1,21 @@
+/* Algorithme de Heap */
+// https://fr.wikipedia.org/wiki/Algorithme_de_Heap
+
+// entrée : un entier k, un tableau A avec k <= taille(A)
+// effet : l'algorithme écrit successivement les k! permutations du tableau A affectant les k premiers indices
+// en appelant l'algorithme avec k = taille(A), on écrit toutes les permutations de A.
+// Ici, le tableau A est indexé de 0 à taille(A)-1.
+fonction Heap(k, A)
+ si k = 1
+ afficher(A)
+ sinon
+ // On génère les permutations fixant A[k-1]
+ Heap(k - 1, A)
+
+ // On génère les permutations échangeant A[k-1] avec les autres termes.
+ pour i de 0 a k-2
+ si k est pair
+ échanger(A[i], A[k-1])
+ sinon
+ échanger(A[0], A[k-1])
+ Heap(k - 1, A)
\ No newline at end of file
diff --git a/cours/16 - Permutations/algo-recursif.js b/cours/16 - Permutations/algo-recursif.js
new file mode 100644
index 0000000..e69de29
diff --git a/cours/16 - Permutations/algo-recursif.pseudo b/cours/16 - Permutations/algo-recursif.pseudo
new file mode 100644
index 0000000..d28c45e
--- /dev/null
+++ b/cours/16 - Permutations/algo-recursif.pseudo
@@ -0,0 +1,24 @@
+/* Algorithme récursif de permutations */
+
+// Cas de test ["1","2","3"]
+// => ["123", "132", "213", "231", "312", "321"]
+
+fonction permutations(tableau)
+ // Cas de base
+ si tableau.longueur = 1
+ retourner [tableau[0]]
+ // Cas de récursion
+ solutions = []
+ pour i de 0 a tableau.longueur - 1
+ // 1(23)
+ // tableau = ["1","2","3"] et i = 0
+ autresElements = supprimerElementEnPosition(tableau, i)
+ // autresElements = ["2", "3"]
+ listePermutations = permutations(autresElements)
+ // listePermutations = (23) = ["23", "32"]
+ pour p de 0 a listePermutations.longueur - 1
+ solutions.ajouter(tableau[i] + listePermutations[p])
+ retourner solutions
+
+// Appel de la fonction
+permutations(["1", "2", "3"])
\ No newline at end of file
diff --git a/cours/16 - Permutations/cours.js b/cours/16 - Permutations/cours.js
new file mode 100644
index 0000000..902b94c
--- /dev/null
+++ b/cours/16 - Permutations/cours.js
@@ -0,0 +1,14 @@
+/* Les permutations */
+
+/* Générer les permutations "à la main" */
+
+// Permutations de l'ensemble E = [1,2] noté (12)
+
+// (1,2,3)
+
+// (1,2,3,4)
+
+// Formule du dénombrement
+
+// Voir https://excalidraw.com/#json=cusHm33AAUv2Z4HZ5_eYG,vh5Lz-WZd35Zq8sAyN9dBg
+
diff --git a/cours/16 - Permutations/solutions/algo-heap.js b/cours/16 - Permutations/solutions/algo-heap.js
new file mode 100644
index 0000000..26f7678
--- /dev/null
+++ b/cours/16 - Permutations/solutions/algo-heap.js
@@ -0,0 +1,23 @@
+function Heap(k, A) {
+ if (k === 1) {
+ console.log(A);
+ } else {
+ // On génère les permutations fixant A[k-1]
+ Heap(k - 1, A);
+
+ // On génère les permutations échangeant A[k-1] avec les autres termes.
+ for (let i = 0; i < k - 1; i++) {
+ if (k % 2 === 0) {
+ let temporaire = A[k-1];
+ A[k-1] = A[i];
+ A[i] = temporaire;
+ } else {
+ [A[0], A[k-1]] = [A[k-1], A[0]];
+ }
+ Heap(k - 1, A);
+ }
+ }
+}
+
+A = [1,2,3];
+Heap(A.length, A);
\ No newline at end of file
diff --git a/cours/16 - Permutations/solutions/algo-recursif.js b/cours/16 - Permutations/solutions/algo-recursif.js
new file mode 100644
index 0000000..c1d1cc9
--- /dev/null
+++ b/cours/16 - Permutations/solutions/algo-recursif.js
@@ -0,0 +1,24 @@
+function permutations(tableau) {
+ // Cas de base
+ if (tableau.length === 1) {
+ return [tableau[0]];
+ }
+ // Cas de récursion
+ let solutions = [];
+ for (let i = 0; i < tableau.length; i++) {
+ // 1(23)
+ // tableau = ["1","2","3"] et i = 0
+ let autresElements = [...tableau];
+ autresElements.splice(i, 1);
+ // autresElements = ["2", "3"]
+ let listePermutations = permutations(autresElements);
+ // listePermutations = (23) = ["23", "32"]
+ for (let p = 0; p < listePermutations.length; p++) {
+ solutions.push(tableau[i] + listePermutations[p]);
+ }
+ }
+ return solutions;
+}
+
+// Appel de la fonction
+console.log(permutations(["1", "2", "3"]));
\ No newline at end of file
diff --git a/cours/17 - Arrangements/algo-arrangements.js b/cours/17 - Arrangements/algo-arrangements.js
new file mode 100644
index 0000000..e69de29
diff --git a/cours/17 - Arrangements/algo-arrangements.pseudo b/cours/17 - Arrangements/algo-arrangements.pseudo
new file mode 100644
index 0000000..4427a19
--- /dev/null
+++ b/cours/17 - Arrangements/algo-arrangements.pseudo
@@ -0,0 +1,22 @@
+/*
+1 + arrangements(2, [2,3,4])
+ 12 + arrangements(1, [3,4])
+ 123
+ 124
+*/
+
+fonction arrangements(k, tableau)
+ // Cas de base
+ si k = 1
+ retourner tableau
+ solutions = []
+ // Cas de récursion
+ pour i de 0 a tableau.longueur - 1
+ // tableau = [2,3,4]
+ sousEnsemble = supprimerElementEnPosition(tableau, i)
+ // sousEnsemble = [3,4] lorsque i = 0
+ listeArrangements = arrangements(k - 1, sousEnsemble)
+ // listeArrangements = ["3", "4"]
+ pour chaque arrangement dans listeArrangements
+ solutions.ajouter(tableau[i] + arrangement)
+ retourner solutions
\ No newline at end of file
diff --git a/cours/17 - Arrangements/cours.js b/cours/17 - Arrangements/cours.js
new file mode 100644
index 0000000..acba285
--- /dev/null
+++ b/cours/17 - Arrangements/cours.js
@@ -0,0 +1,11 @@
+/* Les arrangements (sans répétitions) */
+
+/* Générer les arrangements "à la main" */
+
+// Arrangements de k = 2 éléments dans E = [1,2,3] (n = 3)
+
+// Arrangements de k = 3 éléments dans E = [1,2,3,4] (n = 4)
+
+// Formule du dénombrement
+
+// Voir https://excalidraw.com/#json=uSx4ttjQSWfjDYC7aiWJD,Sz_2t-_7qvlLc09qZC3Jfg
\ No newline at end of file
diff --git a/cours/17 - Arrangements/solutions/algo-arrangements.js b/cours/17 - Arrangements/solutions/algo-arrangements.js
new file mode 100644
index 0000000..000e458
--- /dev/null
+++ b/cours/17 - Arrangements/solutions/algo-arrangements.js
@@ -0,0 +1,21 @@
+function arrangements(k, tableau) {
+ if (k === 1) {
+ // Copie
+ return [...tableau];
+ }
+ let solutions = [];
+ // Cas de récursion
+ for (let i = 0; i < tableau.length; i++) {
+ // tableau = [1,2,3,4]
+ let sousEnsemble = [...tableau];
+ sousEnsemble.splice(i, 1);
+ // sousEnsemble = [2,3,4] lorsque i = 0
+ let listeArrangements = arrangements(k - 1, sousEnsemble);
+ for (let arrangement of listeArrangements) {
+ solutions.push(tableau[i] + arrangement);
+ }
+ }
+ return solutions;
+}
+
+console.log(arrangements(3, ["1","2","3","4"]));
\ No newline at end of file
diff --git a/cours/18 - Combinaisons/algo-combinaisons.js b/cours/18 - Combinaisons/algo-combinaisons.js
new file mode 100644
index 0000000..e69de29
diff --git a/cours/18 - Combinaisons/algo-combinaisons.pseudo b/cours/18 - Combinaisons/algo-combinaisons.pseudo
new file mode 100644
index 0000000..48d69bf
--- /dev/null
+++ b/cours/18 - Combinaisons/algo-combinaisons.pseudo
@@ -0,0 +1,27 @@
+/* k = 3 éléments dans E = [1,2,3,4] dont n = 4
+1 + combinaisons(2, [2,3,4])
+ 12 + combinaisons(1, [3,4])
+ 123
+ 124
+ 13 + combinaisons(1, [4])
+ 134
+2 + combinaisons(2, [3,4])
+ 23 + combinaisons(1, [4])
+ 234
+*/
+
+fonction combinaisons(k, tableau)
+ si k = 1
+ retourner copie(tableau)
+ solutions = []
+ pour i de 0 a tableau.longueur - k
+ // tableau = [1,2,3,4]
+ sousEnsemble = tableau.sousTableauDepuisPosition(i + 1)
+ // sousEnsemble = [2,3,4]
+ listeCombinaisons = combinaisons(k - 1, sousEnsemble)
+ // listeCombinaisons = ["23","24","34"]
+ pour chaque combinaison dans listeCombinaisons
+ solutions.ajouter(tableau[i] + combinaison)
+ retourner solutions
+
+combinaisons(3, ["1","2","3","4"])
\ No newline at end of file
diff --git a/cours/18 - Combinaisons/cours.js b/cours/18 - Combinaisons/cours.js
new file mode 100644
index 0000000..ea30088
--- /dev/null
+++ b/cours/18 - Combinaisons/cours.js
@@ -0,0 +1,38 @@
+/* Les combinaisons */
+
+/* Générer les combinaisons "à la main" */
+
+// Combinaisons de 2 éléments dans l'ensemble E = [1,2,3]
+
+1 + combinaisons(1, [2,3])
+ 12
+ 13
+2 + combinaisons(1, [3])
+ 23
+
+// Combinaisons de k = 3 éléments dans l'ensemble E = [1,2,3,4]
+
+1 + combinaisons(2, [2,3,4])
+ 12 + combinaisons(1, [3,4])
+ 123
+ 124
+ 13 + combinaisons(1, [4])
+ 134
+2 + combinaisons(2, [3,4])
+ 23 + combinaisons(1, [4])
+ 234
+
+// Formule du dénombrement
+// n! / k!(n - k)!
+
+// Juste avant, revenons sur les formules précédentes :
+// Arrangements : n! / (n - k)!
+// Permutations : n!
+
+// La formule sur les permutations est un cas particulier
+// de la formule sur les arrangements
+
+// Combinaisons : ce sont les arrangements sans les permutations !
+
+// Combinaisons : Arrangements / Permutations
+// soit : n! / k!(n - k)!
diff --git a/cours/18 - Combinaisons/solutions/algo-combinaisons.js b/cours/18 - Combinaisons/solutions/algo-combinaisons.js
new file mode 100644
index 0000000..e1edf64
--- /dev/null
+++ b/cours/18 - Combinaisons/solutions/algo-combinaisons.js
@@ -0,0 +1,19 @@
+function combinaisons(k, tableau) {
+ if (k === 1) {
+ return [...tableau];
+ }
+ let solutions = [];
+ for (let i = 0; i <= tableau.length - k; i++) {
+ // tableau = [1,2,3,4]
+ let sousEnsemble = tableau.slice(i + 1);
+ // sousEnsemble = [2,3,4]
+ let listeCombinaisons = combinaisons(k - 1, sousEnsemble);
+ // listeCombinaisons = ["23","24","34"]
+ for (let combinaison of listeCombinaisons) {
+ solutions.push(tableau[i] + combinaison);
+ }
+ }
+ return solutions;
+}
+
+console.log(combinaisons(3, ["1","2","3","4","5"]));
diff --git a/cours/19 - Memoisation/algo.pseudo b/cours/19 - Memoisation/algo.pseudo
new file mode 100644
index 0000000..735ae21
--- /dev/null
+++ b/cours/19 - Memoisation/algo.pseudo
@@ -0,0 +1,17 @@
+// Exemple de pseudo-code de mémoïsation appliquée à une fonction
+
+// Dictionnaire pour stocker les résultats
+resultats = {}
+
+// Fonction gourmande en temps d'exécution
+fonctionLente(parametre)
+ // Si on a déjà calculé le résultat précédemment, on le renvoie
+ si resultats[parametre] existe
+ retourner resultats[parametre]
+ sinon
+ // On calcule le résultat
+ resultat = ... /* calcul gourmand en temps */
+ // On stocke le resultat dans le dictionnaire
+ resultats[parametre] = resultat
+ // On renvoie le resultat
+ retourner resultat
\ No newline at end of file
diff --git a/cours/19 - Memoisation/cours.js b/cours/19 - Memoisation/cours.js
new file mode 100644
index 0000000..6eaced0
--- /dev/null
+++ b/cours/19 - Memoisation/cours.js
@@ -0,0 +1,19 @@
+/* Technique d'optimisation : la mémoïsation */
+
+// 🚨 Mémoïsation pas mémorisation !
+
+// Qu'est-ce que c'est ?
+// C'est une technique d'optimisation de la vitesse d'exécution d'une fonction. Elle consiste à mémoriser le résultat d'une fonction pour le renvoyer directement lors d'un prochain appel identique de cette fonction, ceci pour éviter de recalculer à nouveau ce résultat.
+
+// Exemple avec du pseudo-code (fichier algo.pseudo)
+
+// Compromis : complexité spatiale (en espace) VS complexité temporelle
+
+// 🚨 Ne fonctionne qu'avec les fonctions pures !
+// mêmes entrées => fonction pure => toujours les mêmes sorties
+// fonction(a,b,c) => résultat toujours identique
+
+// Exemples :
+// - Calculer le Nième terme de la suite de Fibonacci
+// - Puzzle classique CodinGame nécessitant la mémoisation : 1D Spreadsheet
+// => https://www.codingame.com/ide/puzzle/1d-spreadsheet
\ No newline at end of file
diff --git a/cours/19 - Memoisation/fibonacci-memoisation.js b/cours/19 - Memoisation/fibonacci-memoisation.js
new file mode 100644
index 0000000..0b27426
--- /dev/null
+++ b/cours/19 - Memoisation/fibonacci-memoisation.js
@@ -0,0 +1,30 @@
+/* Retourner le Nième terme de la suite de Fibonacci */
+
+// Explication visuelle sur Excalidraw :
+// https://excalidraw.com/#json=50eOA03mDu7cPoR81ADK7,LDjgORQCk236Y3Yk8FpTrA
+
+/* Sans mémoïsation... */
+let resultats = new Map();
+function fibonacci(n) {
+ // Si on a déjà calculé le résultat précédemment, on le renvoie
+ if (resultats.has(n)) {
+ return resultats.get(n);
+ }
+ // Les 2 premiers termes de la suite sont 1
+ let resultat;
+ if (n === 1 || n === 2) {
+ resultat = 1;
+ } else {
+ // Le terme suivant est la somme des 2 termes précédents
+ resultat = fibonacci(n - 1) + fibonacci(n - 2);
+ }
+ // Stocker le résultat dans le dictionnaire
+ resultats.set(n, resultat);
+ return resultat;
+}
+
+// Retourne les 100 premiers termes de la suite de Fibonacci
+for (let n = 1; n <= 100; n++) {
+ console.log(`${n} : ${fibonacci(n)}`);
+}
+
diff --git a/cours/19 - Memoisation/fibonacci.js b/cours/19 - Memoisation/fibonacci.js
new file mode 100644
index 0000000..01f8eb2
--- /dev/null
+++ b/cours/19 - Memoisation/fibonacci.js
@@ -0,0 +1,20 @@
+/* Retourner le Nième terme de la suite de Fibonacci */
+
+// Explication visuelle sur Excalidraw :
+// https://excalidraw.com/#json=50eOA03mDu7cPoR81ADK7,LDjgORQCk236Y3Yk8FpTrA
+
+/* Sans mémoïsation... */
+function fibonacci(n) {
+ // Les 2 premiers termes de la suite sont 1
+ if (n === 1 || n === 2) {
+ return 1;
+ }
+ // Le terme suivant est la somme des 2 termes précédents
+ return fibonacci(n - 1) + fibonacci(n - 2);
+}
+
+// Retourne les 100 premiers termes de la suite de Fibonacci
+for (let n = 1; n <= 100; n++) {
+ console.log(`${n} : ${fibonacci(n)}`);
+}
+
diff --git a/cours/commun/init.js b/cours/commun/init.js
new file mode 100644
index 0000000..c26033f
--- /dev/null
+++ b/cours/commun/init.js
@@ -0,0 +1,31 @@
+/*
+ * N'hésitez pas à jeter un oeil 👁 sur ce code et à me prévenir
+ * si vous rencontrez un bogue !
+ *
+ * Vous pouvez me joindre par email sur jeremy@javascriptdezero.com
+ *
+ * Merci 👍
+ */
+
+/* globals enonces,document */
+
+// On récupère les blocs d'énoncés dans un tableau
+{
+ let numeroEnonce = 0;
+ let enonce;
+ do {
+ enonce = document.getElementById(`enonce-${numeroEnonce}`);
+ if (enonce !== null) {
+ enonces.liste.push(enonce);
+ numeroEnonce += 1;
+ }
+ } while (enonce);
+}
+
+// On initialise le statut des énoncés
+function initialiserEnonces() {
+ enonces.liste.forEach((enonce, i) => {
+ enonces.definirAttente(enonces.liste[i]);
+ });
+}
+initialiserEnonces();
diff --git a/cours/commun/mes-outils.js b/cours/commun/mes-outils.js
new file mode 100644
index 0000000..0d88a3f
--- /dev/null
+++ b/cours/commun/mes-outils.js
@@ -0,0 +1,83 @@
+/*
+ * N'hésitez pas à jeter un oeil 👁 sur ce code et à me prévenir
+ * si vous rencontrez un bogue !
+ *
+ * Vous pouvez me joindre par email sur jeremy@javascriptdezero.com
+ *
+ * Merci 👍
+ */
+
+/* eslint-disable no-unused-vars */
+const classesCSS = {
+ enonce: 'enonce',
+ message: 'message',
+ succes: 'succes',
+ echec: 'echec',
+};
+
+const enonces = {
+ attente: 'En attente de code à tester... À vous de jouer !',
+ echec: "Ce n'est pas la bonne réponse... 😭 Réessayez !",
+
+ extraireMessage(enonce) {
+ return enonce.getElementsByClassName(classesCSS.message)[0];
+ },
+ definirAttente(enonce) {
+ enonce.classList.remove(classesCSS.echec);
+ enonce.classList.remove(classesCSS.succes);
+ enonce.classList.add(classesCSS.enonce);
+ const message = this.extraireMessage(enonce);
+ if (message !== undefined) {
+ message.classList = classesCSS.message;
+ message.innerHTML = this.attente;
+ }
+ },
+ definirSucces(enonce, bonneReponse) {
+ enonce.classList.remove(classesCSS.echec);
+ enonce.classList.add(classesCSS.succes);
+ const message = this.extraireMessage(enonce);
+ if (message !== undefined) {
+ message.classList.remove(classesCSS.echec);
+ message.classList.add(classesCSS.succes);
+ message.innerHTML = `▶︎ Bonne réponse 👍
`; + } + }, + definirEchec(enonce, valeurs) { + enonce.classList.remove(classesCSS.succes); + enonce.classList.add(classesCSS.echec); + const message = this.extraireMessage(enonce); + if (message !== undefined) { + message.classList.remove(classesCSS.succes); + message.classList.add(classesCSS.echec); + } + const { valeurRecue, valeurAttendue } = valeurs; + message.innerHTML = debutMessageEchec(valeurRecue); + message.innerHTML += `▶︎ Alors qu'elle devrait renvoyer :
${formaterPourLeHtml(valeurAttendue)}+
${this.echec}
`; + }, + liste: [], +}; + +function debutMessageEchec(valeurRecue) { + if (valeurRecue === undefined) { + return `▶︎ Votre fonction ne renvoie rien (undefined) !
Êtes-vous sûr d'avoir utilisé l'instruction return
?
▶︎ Votre fonction provoque une erreur 😅 :
${valeurRecue.message}
Êtes-vous sûr d'avoir pensé au cas de base ?` + } + return `
▶︎ Votre fonction renvoie :
${formaterPourLeHtml(valeurRecue)}` + } +} + +function formaterPourLeHtml(valeur) { + return ( + String(valeur) + // Les espaces en trop en fin et début de ligne sont mis en surbrillance + .replace(/( {2,})/g, '$1') + .replace(/^( +)/g, '$1') + .replace(/( +)$/g, '$1') + .replace(/( +)\n/g, '$1\n') + .replace(/\n/g, '