0Pricing
Coding Interview Prep · Leçon

Entretien blanc chronométré : problèmes faciles et intermédiaires

Résolvez trois problèmes avec une limite de 45 minutes, verbalisez votre raisonnement comme lors d’un véritable entretien, puis examinez les solutions optimales.

Entretien blanc chronométré : problèmes faciles et intermédiaires est une leçon Coding Interview Prep gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Coding Interview Prep, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Coding Interview Prep comprend 4 leçons au total.

Comment utiliser cet entretien simulé

Cette leçon simule une véritable session d'entretien de programmation. Pour chaque problème, vous devriez : (1) le lire une fois, (2) identifier le motif en moins de 60 secondes, (3) présenter votre approche et sa complexité, (4) écrire la solution et (5) effectuer un test avec des exemples. Lancez un minuteur. Un problème facile devrait prendre 10 à 15 minutes ; un problème moyen, 20 à 25 minutes.

Ne regardez pas la solution à l'avance : cela va à l'encontre de l'objectif. Si vous êtes bloqué après 5 minutes, relisez l'énoncé et recherchez le mot signal qui révèle le motif (trié ? minimum ? toutes les combinaisons ? sous-tableau ?). La capacité à vous débloquer seul est aussi importante que celle à résoudre rapidement le problème.

# Mock interview timer simulation
import time

class InterviewTimer:
    def __init__(self, total_minutes):
        self.total = total_minutes * 60
        self.start = None

    def begin(self, problem_name):
        self.start = time.time()
        print(f'TIMER STARTED: {problem_name}')
        print(f'You have {self.total//60} minutes. Go!')

    def checkpoint(self, label):
        if self.start:
            elapsed = time.time() - self.start
            remaining = self.total - elapsed
            print(f'[{label}] Elapsed: {elapsed:.0f}s, Remaining: {remaining:.0f}s')

# Usage in real practice:
timer = InterviewTimer(15)  # 15-minute easy problem
timer.begin('Two Sum')
time.sleep(1)
timer.checkpoint('Identified pattern')

Problème facile 1 : parenthèses valides

Problème : Étant donné une chaîne contenant uniquement '(', ')', '{', '}', '[' et ']', déterminez si la chaîne d'entrée est valide. Une chaîne est valide si chaque parenthèse ouvrante est fermée par une parenthèse du même type, dans le bon ordre.

Signal : les paires doivent correspondre, l'ordre est important et la parenthèse ouvrante la plus récente doit être fermée en premier → Pile. Empilez les parenthèses ouvrantes ; utilisez pop et vérifiez les parenthèses fermantes. Si la pile est vide lorsque vous essayez de retirer un élément, ou si elle contient encore des éléments à la fin, la chaîne n'est pas valide. Temps O(n), espace O(n).

def is_valid(s):
    stack = []
    matching = {')': '(', '}': '{', ']': '['}

    for char in s:
        if char in '({[':
            stack.append(char)
        else:
            if not stack or stack[-1] != matching[char]:
                return False
            stack.pop()
    return len(stack) == 0

# Test cases
test_cases = [
    ('()', True),
    ('()[]{}'  , True),
    ('(]', False),
    ('([)]', False),
    ('{[]}', True),
    ('', True),        # empty string is valid
    ('(((', False),    # unmatched opens
    (')]', False),     # close without open
]
for s, expected in test_cases:
    result = is_valid(s)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: is_valid({repr(s)}) = {result} (expected {expected})')

Problème facile 2 : meilleur moment pour acheter et vendre des actions

Problème : Étant donné un tableau prices où prices[i] est le cours de l'action au jour i, trouvez le bénéfice maximal obtenu avec un achat et une vente (l'achat doit précéder la vente). Renvoyez 0 si aucun bénéfice n'est possible.

Signal : différence maximale où la valeur de gauche doit précéder celle de droite → Suivez le minimum courant en parcourant le tableau de gauche à droite. Chaque jour, le bénéfice potentiel est current_price - min_so_far. Mettez à jour le bénéfice maximal. Cette solution est en O(n)/O(1) et constitue un cas particulier de l'algorithme de Kadane.

def max_profit(prices):
    if not prices:
        return 0
    min_price = float('inf')
    max_profit = 0

    for price in prices:
        if price < min_price:
            min_price = price
        elif price - min_price > max_profit:
            max_profit = price - min_price
    return max_profit

# Test cases
test_cases = [
    ([7, 1, 5, 3, 6, 4], 5),   # buy at 1, sell at 6
    ([7, 6, 4, 3, 1], 0),      # monotonically decreasing: no profit
    ([2, 4, 1], 2),             # buy at 2, sell at 4
    ([1], 0),                   # single price: no transaction possible
    ([3, 3, 3], 0),             # flat: no profit
]
for prices, expected in test_cases:
    result = max_profit(prices)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: max_profit({prices}) = {result} (expected {expected})')

Problème moyen 1 : somme de trois éléments

Problème : Étant donné un tableau, trouvez tous les triplets uniques dont la somme est nulle. La solution ne doit pas contenir de triplets en double.

Motif : deux pointeurs étendus à trois éléments. Triez le tableau. Pour chaque élément nums[i], utilisez deux pointeurs left = i+1 et right = n-1 pour trouver les paires dont la somme est égale à -nums[i]. Ignorez les doublons en avançant au-delà des valeurs identiques. Temps O(n²), espace O(1) sans compter la sortie. Le tri facilite la gestion des doublons.

def three_sum(nums):
    nums.sort()
    result = []
    n = len(nums)

    for i in range(n - 2):
        # Skip duplicate values for the first element
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        left, right = i + 1, n - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total == 0:
                result.append([nums[i], nums[left], nums[right]])
                while left < right and nums[left] == nums[left + 1]:
                    left += 1      # skip duplicate lefts
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1     # skip duplicate rights
                left += 1; right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1
    return result

print(three_sum([-1, 0, 1, 2, -1, -4]))  # [[-1,-1,2],[-1,0,1]]
print(three_sum([0, 0, 0, 0]))            # [[0,0,0]]
print(three_sum([]))                       # []
print(three_sum([1, 2, -2, -1]))           # []

Problème intermédiaire 2 : Sous-chaîne la plus longue sans caractères répétés

Problème : Étant donné une chaîne, trouvez la longueur de la plus longue sous-chaîne sans caractères répétés.

Schéma : Fenêtre glissante avec un ensemble (ou un dictionnaire des dernières positions). Maintenez une fenêtre [gauche, droite]. Déplacez la borne droite en incluant chaque caractère. Si un caractère se répète (s'il est déjà dans la fenêtre), réduisez la fenêtre par la gauche jusqu'à supprimer le doublon. Conservez la taille maximale de fenêtre observée. Temps O(n), espace O(min(n, taille de l'alphabet)).

def length_of_longest_substring(s):
    char_index = {}    # character -> last seen index
    left = 0
    max_len = 0

    for right, char in enumerate(s):
        if char in char_index and char_index[char] >= left:
            left = char_index[char] + 1  # shrink window past duplicate
        char_index[char] = right
        max_len = max(max_len, right - left + 1)
    return max_len

# Test cases
test_cases = [
    ('abcabcbb', 3),   # 'abc'
    ('bbbbb', 1),       # 'b'
    ('pwwkew', 3),      # 'wke'
    ('', 0),            # empty string
    ('au', 2),          # full string
    ('dvdf', 3),        # 'vdf' (skip the first d)
]
for s, expected in test_cases:
    result = length_of_longest_substring(s)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: len_longest({repr(s)}) = {result} (expected {expected})')

Problème intermédiaire 3 : Rendre la monnaie

Problème : Étant donné des valeurs de pièces et une somme cible, trouvez le nombre minimal de pièces nécessaires pour atteindre cette somme. Renvoyez -1 si cela est impossible.

Schéma : DP classique en une dimension (variante du problème du sac à dos sans limite). dp[i] = nombre minimal de pièces pour la somme i. Initialisez dp[0] = 0 et toutes les autres valeurs à l'infini. Pour chaque somme de 1 à la somme cible, essayez toutes les valeurs de pièces. dp[i] = min(dp[i], dp[i - coin] + 1) pour chaque pièce valide. Temps O(somme × nombre_de_pièces), espace O(somme).

def coin_change(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0   # 0 coins to make amount 0

    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i and dp[i - coin] + 1 < dp[i]:
                dp[i] = dp[i - coin] + 1

    return dp[amount] if dp[amount] != float('inf') else -1

# Test cases
test_cases = [
    ([1, 5, 11], 15, 3),      # 11+1+1+1+1... wait: 11+1+1+1+1=5 coins? No: 5+5+5=3
    ([2], 3, -1),              # impossible (only even coins)
    ([1], 0, 0),               # 0 coins for amount 0
    ([1, 2, 5], 11, 3),        # 5+5+1
    ([186, 419, 83, 408], 6249, 20),  # stress test
]
for coins, amount, expected in test_cases:
    result = coin_change(coins, amount)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: coin_change({coins}, {amount}) = {result} (expected {expected})')

Méthode de résolution sous la pression du temps

Lorsque le temps presse, donnez la priorité, dans cet ordre : (1) à une solution par force brute fonctionnelle donnant un résultat correct plutôt qu'à une solution optimale incomplète, (2) à la gestion visible des cas limites, (3) à l'écriture d'un code clair et lisible plutôt qu'à des expressions ingénieuses sur une seule ligne. Les recruteurs préfèrent une solution claire en O(n²) qui réussit tous les cas d'essai à une solution en O(n) comportant une erreur subtile.

Si vous vous rendez compte que votre solution en O(n²) est incorrecte, ne l'abandonnez pas en cours de route : terminez-la, vérifiez-la, puis proposez de l'optimiser s'il vous reste du temps. Une solution optimale à moitié écrite rapporte moins de points qu'une solution complète mais sous-optimale.

# Priority order when time runs out
priority = [
    ('First priority',  'Correct brute-force that passes all test cases'),
    ('Second priority', 'Optimal solution with bugs is WORSE than suboptimal correct'),
    ('Third priority',  'Edge cases handled visibly (empty input, single element, negatives)'),
    ('Fourth priority', 'Clean variable names and readable code'),
    ('Fifth priority',  'Add complexity statement as a comment at the top'),
]
print('Under time pressure, prioritise:')
for priority_level, desc in priority:
    print(f'  {priority_level}: {desc}')

# Adding complexity as a comment
def two_sum_commented(nums, target):
    # Time: O(n), Space: O(n)
    seen = {}
    for i, n in enumerate(nums):
        complement = target - n
        if complement in seen:
            return [seen[complement], i]
        seen[n] = i
    return []

Réviser votre solution : cinq questions

Avant de dire « J'ai terminé », posez-vous ces cinq questions :

  1. Gère-t-elle une entrée vide ? [], '', None, n=0
  2. Gère-t-elle un seul élément ? Tableaux de taille 1, arbres comportant un seul nœud
  3. Gère-t-elle des éléments tous identiques ? [5, 5, 5, 5], 'aaaa'
  4. Gère-t-elle les values minimales et maximales ? Nombres négatifs, entiers très grands, 0
  5. Ai-je indiqué la complexité temporelle et spatiale ? Notation en O avec une brève justification

Ces cinq vérifications permettent de détecter la majorité des erreurs dans les solutions d'entretien. Les recruteurs s'attendent à ce que les candidats vérifient eux-mêmes leur solution : ils ne vous diront pas qu'elle contient une erreur, sauf si vous demandez un retour.

# The five edge-case categories with examples
edge_cases = {
    'Empty input':     ['[] empty array', '"" empty string', 'None / null'],
    'Single element':  ['[42]', 'single node tree', 'n=1'],
    'All same':        ['[3,3,3,3]', '"aaaa"', 'uniform grid'],
    'Extreme values':  ['[-10^9, 10^9]', 'INT_MAX + 1 overflow check', '0 as input'],
    'Already sorted':  ['ascending + descending', 'already optimal input'],
}
for category, examples in edge_cases.items():
    print(f'{category}:')
    for ex in examples:
        print(f'  - {ex}')
    print()

# Template for self-testing:
def test_my_solution(fn, test_cases):
    for inputs, expected in test_cases:
        result = fn(*inputs) if isinstance(inputs, tuple) else fn(inputs)
        status = 'PASS' if result == expected else 'FAIL'
        print(f'{status}: {inputs} => {result} (expected {expected})')

Gérer les questions complémentaires

Après avoir résolu le problème, les recruteurs posent généralement des questions complémentaires. Voici les types les plus courants :

  • « Pouvez-vous le faire avec un espace O(1) ? » → Cherchez une modification sur place ou des astuces mathématiques
  • « Que faire si n est très grand ? » → Discutez des approches par flux, par pagination ou par échantillonnage
  • « Que faire si le tableau est déjà trié ? » → Il existe souvent un algorithme plus simple
  • « Pouvez-vous paralléliser cette solution ? » → Repérez les sous-problèmes indépendants et discutez de MapReduce ou du parallélisme des tâches

Les questions complémentaires évaluent la profondeur de vos connaissances et votre capacité d'adaptation. Dites « Laissez-moi réfléchir un instant » plutôt que de deviner immédiatement. Une pause réfléchie vaut mieux qu'une mauvaise réponse donnée avec assurance.

# Follow-up answers for classic problems
follow_ups = [
    {
        'problem': 'Find duplicate in array 1..n (space O(n) solution uses set)',
        'follow_up': 'Can you do it in O(1) space without modifying input?',
        'answer': 'Floyd cycle detection: treat array as linked list (slow/fast pointer)',
    },
    {
        'problem': 'Reverse a string (space O(n) with new array)',
        'follow_up': 'Can you do it in-place?',
        'answer': 'Two pointers from both ends, swap until they meet: O(n) time O(1) space',
    },
    {
        'problem': 'Find max in array: O(n) single pass',
        'follow_up': 'What if the array is streamed one element at a time?',
        'answer': 'Same algorithm works! Running maximum handles infinite streams',
    },
    {
        'problem': 'Merge sorted arrays O(n+m)',
        'follow_up': 'What if you have K sorted arrays?',
        'answer': 'Use a min-heap of (value, array_idx, element_idx): O(n log k)',
    },
]
for fu in follow_ups:
    print(f'Problem: {fu["problem"]}')
    print(f'Follow-up: {fu["follow_up"]}')
    print(f'Answer: {fu["answer"]}\n')

Exercice : regrouper les anagrammes

Problème : Étant donné un tableau de chaînes, regroupez les anagrammes. Renvoyez une liste de groupes.

Schéma : Utilisez une table de fréquences comme clé. Pour chaque chaîne, triez ses caractères (ou calculez un tuple de fréquences de caractères) afin d'obtenir la clé canonique. Regroupez les chaînes selon cette clé à l'aide d'une table de hachage contenant des listes. Temps O(n × m log m), où m est la longueur maximale d'une chaîne, espace O(n × m). Aucune boucle imbriquée n'est nécessaire : parcourez le tableau une seule fois.

from collections import defaultdict

def group_anagrams(strs):
    # Method 1: sort each string as key
    groups = defaultdict(list)
    for s in strs:
        key = ''.join(sorted(s))   # canonical form
        groups[key].append(s)
    return list(groups.values())

def group_anagrams_v2(strs):
    # Method 2: character count tuple as key (avoids sorting)
    groups = defaultdict(list)
    for s in strs:
        count = [0] * 26
        for c in s:
            count[ord(c) - ord('a')] += 1
        key = tuple(count)   # immutable, hashable
        groups[key].append(s)
    return list(groups.values())

test = ['eat', 'tea', 'tan', 'ate', 'nat', 'bat']
result = [sorted(g) for g in group_anagrams(test)]
result.sort()
print('Groups:', result)
# [['ate','eat','tea'], ['bat'], ['nat','tan']]

print('V2:', [sorted(g) for g in sorted(group_anagrams_v2(test), key=len)])

Autoévaluation après une simulation

Après chaque entretien simulé, évaluez-vous selon les dimensions suivantes :

  • Vitesse de reconnaissance des schémas : Avez-vous identifié le schéma en <60 secondes ?
  • Correction du code : Votre première solution réussissait-elle toutes les vérifications ?
  • Gestion des cas limites : Avez-vous vérifié les entrées vides, à un seul élément ou extrêmes ?
  • Communication : Avez-vous expliqué votre raisonnement tout au long de l'exercice ?
  • Conscience de la complexité : Avez-vous indiqué les complexités temporelle et spatiale ?
  • Récupération : Si vous étiez bloqué, avez-vous su changer d'approche avec aisance ou vous êtes-vous figé ?

Attribuez-vous une note de 1 à 5 pour chaque dimension. Consacrez votre semaine d'entraînement suivante à la dimension qui a obtenu la note la plus basse. La plupart des candidats doivent améliorer soit la reconnaissance des schémas, soit la communication, mais rarement les deux.

# Self-assessment scoring template
def self_assess(pattern_speed, code_correctness, edge_cases,
                communication, complexity, recovery):
    scores = {
        'Pattern recognition (< 60s)': pattern_speed,
        'Code correctness (all tests pass)': code_correctness,
        'Edge case handling': edge_cases,
        'Communication (thinking aloud)': communication,
        'Complexity stated correctly': complexity,
        'Recovery when stuck': recovery,
    }
    total = sum(scores.values())
    max_total = len(scores) * 5
    print('Self-Assessment Results:')
    print('-'*50)
    for dim, score in scores.items():
        bar = '#' * score + '-' * (5 - score)
        print(f'{dim:45s} [{bar}] {score}/5')
    print(f'\nTotal: {total}/{max_total} ({total/max_total*100:.0f}%)')
    weak = min(scores, key=scores.get)
    print(f'Focus area: {weak}')

self_assess(4, 3, 4, 3, 5, 2)  # example scores

Vérification rapide

Vérifiez votre compréhension des notions de Structures de données et algorithmes — préparation à l'entretien de programmation présentées dans cette leçon.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris : à aborder les problèmes selon une méthode fixe : lire l'énoncé, identifier le schéma en 60 secondes, indiquer la complexité, écrire le code, puis le vérifier selon cinq catégories de cas limites, qu'une solution par force brute qui fonctionne vaut mieux qu'une solution optimale incomplète lorsque le temps presse, et qu'une autoévaluation après chaque séance d'entraînement simulée, selon six dimensions (vitesse, correction, cas limites, communication, complexité, récupération), permet de concentrer les efforts sur les bons axes d'amélioration. Dans la prochaine leçon, nous étudierons en détail la gestion des cas limites et les bonnes pratiques de communication du candidat.

Questions Fréquemment Posées

La leçon « Entretien blanc chronométré : problèmes faciles et intermédiaires » est-elle gratuite ?

Oui — le texte complet de « Entretien blanc chronométré : problèmes faciles et intermédiaires » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Coding Interview Prep, passe à CoddyKit PRO. Le cours Coding Interview Prep comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Entretien blanc chronométré : problèmes faciles et intermédiaires » ?

Résolvez trois problèmes avec une limite de 45 minutes, verbalisez votre raisonnement comme lors d’un véritable entretien, puis examinez les solutions optimales. Tu pratiques Coding Interview Prep avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Coding Interview Prep ?

Aucune expérience préalable n'est requise. Coding Interview Prep sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.

Combien de temps prend la leçon « Entretien blanc chronométré : problèmes faciles et intermédiaires » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Coding Interview Prep ?

Oui. Chaque leçon Coding Interview Prep inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Aide-mémoire de reconnaissance des schémas
  2. Entretien blanc chronométré : problèmes faciles et intermédiaires
  3. Gestion des cas limites et communication avec la personne interrogée
  4. Étude de problèmes difficiles : Word Ladder II et Alien Dictionary
← Retour à Coding Interview Prep